diff --git a/.gitattributes b/.gitattributes index 2062142284..d0cf036126 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ **/snapshots/**/*.png filter=lfs diff=lfs merge=lfs -text **/docs/images-lfs/*.png filter=lfs diff=lfs merge=lfs -text +libraries/mediaupload/impl/src/test/assets/* filter=lfs diff=lfs merge=lfs -text diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 893a4298c7..73f8c96ebc 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -1,5 +1,5 @@ name: Bug report for the Element X Android app -description: Report any issues that you have found with the Element X app. Please [check open issues](https://github.com/vector-im/element-x-android/issues) first, in case it has already been reported. +description: Report any issues that you have found with the Element X app. Please [check open issues](https://github.com/element-hq/element-x-android/issues) first, in case it has already been reported. labels: [T-Defect] body: - type: markdown diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml index 0e51d5155e..114bc2bc65 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yml +++ b/.github/ISSUE_TEMPLATE/enhancement.yml @@ -5,7 +5,7 @@ body: - type: markdown attributes: value: | - Thank you for taking the time to propose an enhancement to an existing feature. If you would like to propose a new feature or a major cross-platform change, please [start a discussion here](https://github.com/vector-im/element-meta/discussions/new?category=ideas). + Thank you for taking the time to propose an enhancement to an existing feature. If you would like to propose a new feature or a major cross-platform change, please [start a discussion here](https://github.com/element-hq/element-meta/discussions/new?category=ideas). - type: textarea id: usecase attributes: diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 29542e98c6..ec67e56676 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,7 +13,7 @@ updates: ignore: - dependency-name: "*" reviewers: - - "vector-im/element-x-android-reviewers" + - "element-hq/element-x-android-reviewers" # Updates for Gradle dependencies used in the app - package-ecosystem: "gradle" directory: "/" @@ -23,4 +23,4 @@ updates: ignore: - dependency-name: "*" reviewers: - - "vector-im/element-x-android-reviewers" + - "element-hq/element-x-android-reviewers" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 7aabcb77aa..29c7e163f4 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,4 +1,4 @@ - + ## Type of change @@ -53,9 +53,9 @@ Uncomment this markdown table below and edit the last line `|||`: - [ ] Changes have been tested on an Android device or Android emulator with API 23 - [ ] UI change has been tested on both light and dark themes -- [ ] Accessibility has been taken into account. See https://github.com/vector-im/element-x-android/blob/develop/CONTRIBUTING.md#accessibility +- [ ] Accessibility has been taken into account. See https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md#accessibility - [ ] Pull request is based on the develop branch -- [ ] Pull request includes a new file under ./changelog.d. See https://github.com/vector-im/element-x-android/blob/develop/CONTRIBUTING.md#changelog +- [ ] Pull request includes a new file under ./changelog.d. See https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md#changelog - [ ] Pull request includes screenshots or videos if containing UI changes - [ ] Pull request includes a [sign off](https://matrix-org.github.io/synapse/latest/development/contributing_guide.html#sign-off) - [ ] You've made a self review of your PR diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 63ac00e289..e220ec76e4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,12 +33,12 @@ jobs: # https://github.com/actions/checkout/issues/881 ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} - name: Use JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v2.9.0 + uses: gradle/gradle-build-action@v2.11.1 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Assemble debug APK @@ -50,7 +50,7 @@ jobs: run: ./gradlew :app:assembleDebug -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES - name: Upload APK APKs if: ${{ matrix.variant == 'debug' }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: elementx-debug path: | diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml index 772df369b8..e1c9205473 100644 --- a/.github/workflows/danger.yml +++ b/.github/workflows/danger.yml @@ -11,7 +11,7 @@ jobs: - run: | npm install --save-dev @babel/plugin-transform-flow-strip-types - name: Danger - uses: danger/danger-js@11.3.0 + uses: danger/danger-js@11.3.1 with: args: "--dangerfile ./tools/danger/dangerfile.js" env: diff --git a/.github/workflows/gradle-wrapper-update.yml b/.github/workflows/gradle-wrapper-update.yml index 351057fc89..b3493e3bf7 100644 --- a/.github/workflows/gradle-wrapper-update.yml +++ b/.github/workflows/gradle-wrapper-update.yml @@ -12,7 +12,7 @@ jobs: - name: Update Gradle Wrapper uses: gradle-update/update-gradle-wrapper-action@v1 # Skip in forks - if: github.repository == 'vector-im/element-x-android' + if: github.repository == 'element-hq/element-x-android' with: repo-token: ${{ secrets.GITHUB_TOKEN }} target-branch: develop diff --git a/.github/workflows/maestro.yml b/.github/workflows/maestro.yml index ad13d5ecc7..a4bf0cc5ae 100644 --- a/.github/workflows/maestro.yml +++ b/.github/workflows/maestro.yml @@ -1,11 +1,10 @@ name: Maestro -# Run this flow only on pull request, and only when the pull request has been approved, to limit our usage of maestro cloud. -# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#running-a-workflow-when-a-pull-request-is-approved +# Run this flow only on pull request, and only when the pull request has the Run-Maestro label, to limit our usage of maestro cloud. on: workflow_dispatch: - pull_request_review: - types: [submitted] + pull_request: + types: [labeled] # Enrich gradle.properties for CI/CD env: @@ -16,7 +15,7 @@ jobs: maestro-cloud: name: Maestro test suite runs-on: ubuntu-latest - if: github.event.review.state == 'approved' || github.event_name == 'workflow_dispatch' + if: github.event_name == 'workflow_dispatch' || github.event.label.name == 'Run-Maestro' strategy: fail-fast: false # Allow one per PR. @@ -24,12 +23,17 @@ jobs: group: ${{ format('maestro-{0}', github.ref) }} cancel-in-progress: true steps: + - name: Remove Run-Maestro label + if: ${{ github.event.label.name == 'Run-Maestro' }} + uses: actions-ecosystem/action-remove-labels@v1 + with: + labels: Run-Maestro - uses: actions/checkout@v4 with: # Ensure we are building the branch and not the branch after being merged on develop # https://github.com/actions/checkout/issues/881 ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 name: Use JDK 17 with: distribution: 'temurin' # See 'Supported distributions' for available options diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 7f551cb981..91976f56a0 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -14,11 +14,11 @@ jobs: nightly: name: Build and publish nightly bundle to Firebase runs-on: ubuntu-latest - if: ${{ github.repository == 'vector-im/element-x-android' }} + if: ${{ github.repository == 'element-hq/element-x-android' }} steps: - uses: actions/checkout@v4 - name: Use JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' diff --git a/.github/workflows/nightlyReports.yml b/.github/workflows/nightlyReports.yml index 5562a67cec..3cb4119f99 100644 --- a/.github/workflows/nightlyReports.yml +++ b/.github/workflows/nightlyReports.yml @@ -15,13 +15,13 @@ jobs: nightlyReports: name: Create kover report artifact and upload sonar result. runs-on: ubuntu-latest - if: ${{ github.repository == 'vector-im/element-x-android' }} + if: ${{ github.repository == 'element-hq/element-x-android' }} steps: - name: ⏬ Checkout with LFS uses: nschloe/action-cached-lfs-checkout@v1.2.2 - name: Use JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' @@ -37,7 +37,7 @@ jobs: - name: ✅ Upload kover report if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: kover-results path: | @@ -57,19 +57,19 @@ jobs: steps: - uses: actions/checkout@v4 - name: Use JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v2.9.0 + uses: gradle/gradle-build-action@v2.11.1 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Dependency analysis run: ./gradlew dependencyCheckAnalyze $CI_GRADLE_ARG_PROPERTIES - name: Upload dependency analysis if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: dependency-analysis path: build/reports/dependency-check-report.html diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index eba18e01c1..3a36faf6d3 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -35,19 +35,19 @@ jobs: # https://github.com/actions/checkout/issues/881 ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} - name: Use JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v2.9.0 + uses: gradle/gradle-build-action@v2.11.1 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Run code quality check suite run: ./gradlew runQualityChecks $CI_GRADLE_ARG_PROPERTIES - name: Upload reports if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: linting-report path: | @@ -60,7 +60,7 @@ jobs: yarn add danger-plugin-lint-report --dev - name: Danger lint if: always() - uses: danger/danger-js@11.3.0 + uses: danger/danger-js@11.3.1 with: args: "--dangerfile ./tools/danger/dangerfile-lint.js" env: diff --git a/.github/workflows/recordScreenshots.yml b/.github/workflows/recordScreenshots.yml index 621e91868e..ea86142700 100644 --- a/.github/workflows/recordScreenshots.yml +++ b/.github/workflows/recordScreenshots.yml @@ -2,6 +2,8 @@ name: Record screenshots on: workflow_dispatch: + pull_request: + types: [ labeled ] # Enrich gradle.properties for CI/CD env: @@ -9,26 +11,39 @@ env: jobs: record: - name: Record screenshots on branch ${{ github.ref_name }} + name: Record screenshots on branch ${{ github.event.pull_request.head.ref || github.ref_name }} runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' || github.event.label.name == 'Record-Screenshots' steps: - - name: ⏬ Checkout with LFS + - name: Remove Record-Screenshots label + if: github.event.label.name == 'Record-Screenshots' + uses: actions-ecosystem/action-remove-labels@v1 + with: + labels: Record-Screenshots + - name: ⏬ Checkout with LFS (PR) + if: github.event.label.name == 'Record-Screenshots' + uses: nschloe/action-cached-lfs-checkout@v1.2.2 + with: + persist-credentials: false + ref: ${{ github.event.pull_request.head.ref }} + - name: ⏬ Checkout with LFS (Branch) + if: github.event_name == 'workflow_dispatch' uses: nschloe/action-cached-lfs-checkout@v1.2.2 with: persist-credentials: false - name: ☕️ Use JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' # Add gradle cache, this should speed up the process - name: Configure gradle - uses: gradle/gradle-build-action@v2.9.0 + uses: gradle/gradle-build-action@v2.11.1 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Record screenshots - run: "./.github/workflows/scripts/recordScreenshots.sh" + run: ./.github/workflows/scripts/recordScreenshots.sh env: GITHUB_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }} GITHUB_REPOSITORY: ${{ secrets.GITHUB_REPOSITORY }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1ebc6dd4e3..59b5f60cec 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,12 +20,12 @@ jobs: steps: - uses: actions/checkout@v4 - name: Use JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v2.9.0 + uses: gradle/gradle-build-action@v2.11.1 - name: Create app bundle env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} @@ -33,7 +33,7 @@ jobs: ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} run: ./gradlew bundleRelease $CI_GRADLE_ARG_PROPERTIES - name: Upload bundle as artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: elementx-app-bundle-unsigned path: | diff --git a/.github/workflows/scripts/recordScreenshots.sh b/.github/workflows/scripts/recordScreenshots.sh index 792be75931..5a86a91580 100755 --- a/.github/workflows/scripts/recordScreenshots.sh +++ b/.github/workflows/scripts/recordScreenshots.sh @@ -48,6 +48,9 @@ do esac done +BRANCH=$(git rev-parse --abbrev-ref HEAD) +echo Branch used: $BRANCH + if [[ -z ${TOKEN} ]]; then echo "No token specified, either set the env var GITHUB_TOKEN or use the --token option" exit 1 @@ -70,8 +73,6 @@ git config user.email "benoitm+elementbot@element.io" git add -A git commit -m "Update screenshots" -BRANCH=$(git rev-parse --abbrev-ref HEAD) - echo "Pushing changes" -git push "https://$TOKEN@github.com/$REPO.git" $BRANCH:refs/heads/$BRANCH +git push "https://$TOKEN@github.com/$REPO.git" $BRANCH echo "Done!" diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index e6fdbefa9e..705d937bce 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -27,12 +27,12 @@ jobs: # https://github.com/actions/checkout/issues/881 ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} - name: Use JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v2.9.0 + uses: gradle/gradle-build-action@v2.11.1 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: 🔊 Publish results to Sonar diff --git a/.github/workflows/sync-localazy.yml b/.github/workflows/sync-localazy.yml index f3acb4675b..2f728fdf66 100644 --- a/.github/workflows/sync-localazy.yml +++ b/.github/workflows/sync-localazy.yml @@ -9,11 +9,11 @@ jobs: sync-localazy: runs-on: ubuntu-latest # Skip in forks - if: github.repository == 'vector-im/element-x-android' + if: github.repository == 'element-hq/element-x-android' steps: - uses: actions/checkout@v4 - name: Set up Python 3.9 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.9 - name: Setup Localazy diff --git a/.github/workflows/sync-sas-strings.yml b/.github/workflows/sync-sas-strings.yml new file mode 100644 index 0000000000..52aded770e --- /dev/null +++ b/.github/workflows/sync-sas-strings.yml @@ -0,0 +1,35 @@ +name: Sync SAS strings +on: + workflow_dispatch: + schedule: + # At 00:00 on every Monday UTC + - cron: '0 0 * * 1' + +jobs: + sync-sas-strings: + runs-on: ubuntu-latest + # Skip in forks + if: github.repository == 'element-hq/element-x-android' + # No concurrency required, runs every time on a schedule. + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.8 + uses: actions/setup-python@v5 + with: + python-version: 3.9 + - name: Install Prerequisite dependencies + run: | + pip install requests + - name: Run SAS String script + run: ./tools/sas/import_sas_strings.py + - name: Create Pull Request for SAS Strings + uses: peter-evans/create-pull-request@v5 + with: + commit-message: Sync SAS Strings + title: Sync SAS Strings + body: | + - Update SAS Strings from matrix-doc. + branch: sync-sas-strings + base: develop + + diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 87f46cb47a..6188145d67 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -39,12 +39,12 @@ jobs: # https://github.com/actions/checkout/issues/881 ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} - name: ☕️ Use JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v2.9.0 + uses: gradle/gradle-build-action@v2.11.1 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} @@ -59,7 +59,7 @@ jobs: - name: 🚫 Upload kover failed coverage reports if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: kover-error-report path: | @@ -71,7 +71,7 @@ jobs: - name: 🚫 Upload test results on error if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: tests-and-screenshot-tests-results path: | diff --git a/.github/workflows/triage-incoming.yml b/.github/workflows/triage-incoming.yml index 1232b11d92..109b8cc82d 100644 --- a/.github/workflows/triage-incoming.yml +++ b/.github/workflows/triage-incoming.yml @@ -10,5 +10,5 @@ jobs: steps: - uses: actions/add-to-project@main with: - project-url: https://github.com/orgs/vector-im/projects/91 + project-url: https://github.com/orgs/element-hq/projects/91 github-token: ${{ secrets.ELEMENT_BOT_TOKEN }} diff --git a/.github/workflows/triage-labelled.yml b/.github/workflows/triage-labelled.yml index 5f7e6cc6ec..f6a9de700c 100644 --- a/.github/workflows/triage-labelled.yml +++ b/.github/workflows/triage-labelled.yml @@ -10,11 +10,11 @@ jobs: runs-on: ubuntu-latest # Skip in forks if: > - github.repository == 'vector-im/element-x-android' + github.repository == 'element-hq/element-x-android' steps: - uses: actions/add-to-project@main with: - project-url: https://github.com/orgs/vector-im/projects/43 + project-url: https://github.com/orgs/element-hq/projects/43 github-token: ${{ secrets.ELEMENT_BOT_TOKEN }} move_needs_info: @@ -24,7 +24,7 @@ jobs: - uses: actions/add-to-project@main id: addItem with: - project-url: https://github.com/orgs/vector-im/projects/91 + project-url: https://github.com/orgs/element-hq/projects/91 github-token: ${{ secrets.ELEMENT_BOT_TOKEN }} labeled: X-Needs-Info - name: Print itemId @@ -32,7 +32,7 @@ jobs: - uses: kalgurn/update-project-item-status@main if: ${{ steps.addItem.outputs.itemId }} with: - project-url: https://github.com/orgs/vector-im/projects/91 + project-url: https://github.com/orgs/element-hq/projects/91 github-token: ${{ secrets.ELEMENT_BOT_TOKEN }} item-id: ${{ steps.addItem.outputs.itemId }} status: "Needs info" @@ -45,7 +45,7 @@ jobs: steps: - uses: actions/add-to-project@main with: - project-url: https://github.com/orgs/vector-im/projects/73 + project-url: https://github.com/orgs/element-hq/projects/73 github-token: ${{ secrets.ELEMENT_BOT_TOKEN }} verticals_feature: @@ -56,7 +56,7 @@ jobs: steps: - uses: actions/add-to-project@main with: - project-url: https://github.com/orgs/vector-im/projects/57 + project-url: https://github.com/orgs/element-hq/projects/57 github-token: ${{ secrets.ELEMENT_BOT_TOKEN }} qa: @@ -68,7 +68,7 @@ jobs: steps: - uses: actions/add-to-project@main with: - project-url: https://github.com/orgs/vector-im/projects/69 + project-url: https://github.com/orgs/element-hq/projects/69 github-token: ${{ secrets.ELEMENT_BOT_TOKEN }} signoff: @@ -79,5 +79,5 @@ jobs: steps: - uses: actions/add-to-project@main with: - project-url: https://github.com/orgs/vector-im/projects/89 + project-url: https://github.com/orgs/element-hq/projects/89 github-token: ${{ secrets.ELEMENT_BOT_TOKEN }} diff --git a/.gitignore b/.gitignore index 0b61f0aaaf..d9614fd6e2 100644 --- a/.gitignore +++ b/.gitignore @@ -94,3 +94,5 @@ lint/tmp/ /tmp .DS_Store + +checkouts/** diff --git a/.idea/dictionaries/shared.xml b/.idea/dictionaries/shared.xml index 7afe540d22..c1f91b8411 100644 --- a/.idea/dictionaries/shared.xml +++ b/.idea/dictionaries/shared.xml @@ -2,6 +2,7 @@ backstack + blurhash ftue homeserver konsist diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index e805548aaa..ae3f30ae18 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000000..f8051a6f97 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.maestro/tests/account/changeServer.yaml b/.maestro/tests/account/changeServer.yaml index b56b59e2f1..1971e5a71b 100644 --- a/.maestro/tests/account/changeServer.yaml +++ b/.maestro/tests/account/changeServer.yaml @@ -9,13 +9,10 @@ appId: ${APP_ID} - tapOn: "Other" - tapOn: id: "change_server-server" -# Test server that does not support sliding sync. -- inputText: "https://kieranml.ems-support.element.dev" +- inputText: "element" - hideKeyboard -- tapOn: "kieranml.ems-support.element.dev" -- extendedWaitUntil: - visible: "This server currently doesn’t support sliding sync." - timeout: 10000 -- tapOn: "Cancel" -- back -- back +- tapOn: "element.io" +# Revert to matrix.org +- tapOn: + id: "login-change_server" +- tapOn: "matrix.org" diff --git a/.maestro/tests/roomList/createAndDeleteRoom.yaml b/.maestro/tests/roomList/createAndDeleteRoom.yaml index 18d52b9dd8..9fed7707dd 100644 --- a/.maestro/tests/roomList/createAndDeleteRoom.yaml +++ b/.maestro/tests/roomList/createAndDeleteRoom.yaml @@ -23,7 +23,7 @@ appId: ${APP_ID} - tapOn: text: ${INVITEE2_MXID} index: 1 -- tapOn: "Send" +- tapOn: "Invite" - tapOn: "Back" - tapOn: "aRoomName" - tapOn: "People" diff --git a/AUTHORS.md b/AUTHORS.md index 89404cd73f..b3c2de6470 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,4 +1,4 @@ -A full developer contributors list can be found [here](https://github.com/vector-im/element-x-android/graphs/contributors). +A full developer contributors list can be found [here](https://github.com/element-hq/element-x-android/graphs/contributors). # Core team: diff --git a/CHANGES.md b/CHANGES.md index 3b4b5ca350..494b241823 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,23 +1,74 @@ +Changes in Element X v0.4.0 (2023-12-22) +======================================== + +Features ✨ +---------- + - Use the RTE library `TextView` to render text events in the timeline. Add support for mention pills - with no interaction yet. ([#1433](https://github.com/element-hq/element-x-android/issues/1433)) + - Tapping on a user mention pill opens their profile. ([#1448](https://github.com/element-hq/element-x-android/issues/1448)) + - Display different notifications for mentions. ([#1451](https://github.com/element-hq/element-x-android/issues/1451)) + - Reply to a poll ([#1848](https://github.com/element-hq/element-x-android/issues/1848)) + - Add plain text representation of messages ([#1850](https://github.com/element-hq/element-x-android/issues/1850)) + - Allow polls to be edited when they have not been voted on ([#1869](https://github.com/element-hq/element-x-android/issues/1869)) + - Scroll to end of timeline when sending a new message. ([#1877](https://github.com/element-hq/element-x-android/issues/1877)) + - Confirm back navigation when editing a poll only if the poll was changed ([#1886](https://github.com/element-hq/element-x-android/issues/1886)) + - Add option to delete a poll while editing the poll ([#1895](https://github.com/element-hq/element-x-android/issues/1895)) + - Open room member avatar when you click on it inside the member details screen. ([#1907](https://github.com/element-hq/element-x-android/issues/1907)) + - Poll history of a room is now accessible from the room details screen. ([#2014](https://github.com/element-hq/element-x-android/issues/2014)) + - Always close the invite list screen when there is no more invite. ([#2022](https://github.com/element-hq/element-x-android/issues/2022)) + +Bugfixes 🐛 +---------- + - Fix see room in the room list after leaving it. ([#1006](https://github.com/element-hq/element-x-android/issues/1006)) + - Adjust mention pills font weight and horizontal padding ([#1449](https://github.com/element-hq/element-x-android/issues/1449)) + - Font size in 'All Chats' header was changing mid-animation. ([#1572](https://github.com/element-hq/element-x-android/issues/1572)) + - Accessibility: do not read initial used for avatar out loud. ([#1864](https://github.com/element-hq/element-x-android/issues/1864)) + - Use the right avatar for DMs in DM rooms ([#1912](https://github.com/element-hq/element-x-android/issues/1912)) + - Fix scaling of timeline images: don't crop, don't set min/max aspect ratio values. ([#1940](https://github.com/element-hq/element-x-android/issues/1940)) + - Fix rendering of user name with vertical text by clipping the text. ([#1950](https://github.com/element-hq/element-x-android/issues/1950)) + - Do not render `roomId` if the room has no canonical alias. ([#1970](https://github.com/element-hq/element-x-android/issues/1970)) + - Fix avatar not displayed in notification when the app is not in background ([#1991](https://github.com/element-hq/element-x-android/issues/1991)) + - Fix wording in room invite members view: `Send` -> `Invite`. ([#2037](https://github.com/element-hq/element-x-android/issues/2037)) + - Timestamp positioning was broken, specially for edited messages. ([#2060](https://github.com/element-hq/element-x-android/issues/2060)) + - Emojis in custom reaction bottom sheet are too tiny. ([#2066](https://github.com/element-hq/element-x-android/issues/2066)) + - Set a default power level to join calls. Also, create new rooms taking this power level into account. + +Other changes +------------- + - Add a warning for 'mentions and keywords only' notification option if your homeserver does not support it ([#1749](https://github.com/element-hq/element-x-android/issues/1749)) + - Remove `:libraries:theme` module, extract theme and tokens to [Compound Android](https://github.com/element-hq/compound-android). ([#1833](https://github.com/element-hq/element-x-android/issues/1833)) + - Update poll icons from Compound ([#1849](https://github.com/element-hq/element-x-android/issues/1849)) + - Add ability to see the room avatar in the media viewer. ([#1918](https://github.com/element-hq/element-x-android/issues/1918)) + - RoomList: introduce incremental loading to improve performances. ([#1920](https://github.com/element-hq/element-x-android/issues/1920)) + - Add toggle in the notification settings to disable notifications for room invites. ([#1944](https://github.com/element-hq/element-x-android/issues/1944)) + - Update rendering of Emojis displayed during verification. ([#1965](https://github.com/element-hq/element-x-android/issues/1965)) + - Hide sender info in direct rooms ([#1979](https://github.com/element-hq/element-x-android/issues/1979)) + - Render images in Notification ([#1991](https://github.com/element-hq/element-x-android/issues/1991)) + - Only process content.json from Localazy. ([#2031](https://github.com/element-hq/element-x-android/issues/2031)) + - Always show user avatar in message action sheet ([#2032](https://github.com/element-hq/element-x-android/issues/2032)) + - Hide room list dropdown menu. ([#2062](https://github.com/element-hq/element-x-android/issues/2062)) + - Enable Chat backup, Mentions and Read Receipt in release. ([#2087](https://github.com/element-hq/element-x-android/issues/2087)) + - Make most code used in Compose from `:libraries:matrix` and derived classes Immutable or Stable. + Changes in Element X v0.3.2 (2023-11-22) ======================================== Features ✨ ---------- - - Add ongoing call indicator to rooms lists items. ([#1158](https://github.com/vector-im/element-x-android/issues/1158)) - - Add support for typing mentions in the message composer. ([#1453](https://github.com/vector-im/element-x-android/issues/1453)) - - Add intentional mentions to messages. This needs to be enabled in developer options since it's disabled by default. ([#1591](https://github.com/vector-im/element-x-android/issues/1591)) - - Update voice message recording behaviour. Instead of holding the record button, users can now tap the record button to start recording and tap again to stop recording. ([#1784](https://github.com/vector-im/element-x-android/issues/1784)) + - Add ongoing call indicator to rooms lists items. ([#1158](https://github.com/element-hq/element-x-android/issues/1158)) + - Add support for typing mentions in the message composer. ([#1453](https://github.com/element-hq/element-x-android/issues/1453)) + - Add intentional mentions to messages. This needs to be enabled in developer options since it's disabled by default. ([#1591](https://github.com/element-hq/element-x-android/issues/1591)) + - Update voice message recording behaviour. Instead of holding the record button, users can now tap the record button to start recording and tap again to stop recording. ([#1784](https://github.com/element-hq/element-x-android/issues/1784)) Bugfixes 🐛 ---------- - - Always ensure media temp dir exists ([#1790](https://github.com/vector-im/element-x-android/issues/1790)) + - Always ensure media temp dir exists ([#1790](https://github.com/element-hq/element-x-android/issues/1790)) Other changes ------------- - - Update icons and move away from `PreferenceText` components. ([#1718](https://github.com/vector-im/element-x-android/issues/1718)) - - Add item "This is the beginning of..." at the beginning of the timeline. ([#1801](https://github.com/vector-im/element-x-android/issues/1801)) - - LockScreen : rework LoggedInFlowNode and back management when locked. ([#1806](https://github.com/vector-im/element-x-android/issues/1806)) - - Suppress usage of removeTimeline method. ([#1824](https://github.com/vector-im/element-x-android/issues/1824)) + - Update icons and move away from `PreferenceText` components. ([#1718](https://github.com/element-hq/element-x-android/issues/1718)) + - Add item "This is the beginning of..." at the beginning of the timeline. ([#1801](https://github.com/element-hq/element-x-android/issues/1801)) + - LockScreen : rework LoggedInFlowNode and back management when locked. ([#1806](https://github.com/element-hq/element-x-android/issues/1806)) + - Suppress usage of removeTimeline method. ([#1824](https://github.com/element-hq/element-x-android/issues/1824)) - Remove Element Call feature flag, it's now always enabled. - Reverted the EC base URL to `https://call.element.io`. - Moved the option to override this URL to developer settings from advanced settings. @@ -28,16 +79,16 @@ Changes in Element X v0.3.1 (2023-11-09) Features ✨ ---------- - - Chat backup is still under a feature flag, but when enabled, user can enter their recovery key (it's also possible to input a passphrase) to unlock the encrypted room history. ([#1770](https://github.com/vector-im/element-x-android/pull/1770)) + - Chat backup is still under a feature flag, but when enabled, user can enter their recovery key (it's also possible to input a passphrase) to unlock the encrypted room history. ([#1770](https://github.com/element-hq/element-x-android/pull/1770)) Bugfixes 🐛 ---------- - - Improve confusing text in the 'ready to start verification' screen. ([#879](https://github.com/vector-im/element-x-android/issues/879)) - - Message composer wasn't resized when selecting a several lines message to reply to, then a single line one. ([#1560](https://github.com/vector-im/element-x-android/issues/1560)) + - Improve confusing text in the 'ready to start verification' screen. ([#879](https://github.com/element-hq/element-x-android/issues/879)) + - Message composer wasn't resized when selecting a several lines message to reply to, then a single line one. ([#1560](https://github.com/element-hq/element-x-android/issues/1560)) Other changes ------------- - - PIN: Set lock grace period to 0. ([#1732](https://github.com/vector-im/element-x-android/issues/1732)) + - PIN: Set lock grace period to 0. ([#1732](https://github.com/element-hq/element-x-android/issues/1732)) Changes in Element X v0.3.0 (2023-10-31) @@ -45,24 +96,24 @@ Changes in Element X v0.3.0 (2023-10-31) Features ✨ ---------- - - Element Call: change the 'join call' button in a chat room when there's an active call. ([#1158](https://github.com/vector-im/element-x-android/issues/1158)) - - Mentions: add mentions suggestion view in RTE ([#1452](https://github.com/vector-im/element-x-android/issues/1452)) - - Record and send voice messages ([#1596](https://github.com/vector-im/element-x-android/issues/1596)) - - Enable voice messages for all users ([#1669](https://github.com/vector-im/element-x-android/issues/1669)) - - Receive and play a voice message ([#2084](https://github.com/vector-im/element-x-android/issues/2084)) + - Element Call: change the 'join call' button in a chat room when there's an active call. ([#1158](https://github.com/element-hq/element-x-android/issues/1158)) + - Mentions: add mentions suggestion view in RTE ([#1452](https://github.com/element-hq/element-x-android/issues/1452)) + - Record and send voice messages ([#1596](https://github.com/element-hq/element-x-android/issues/1596)) + - Enable voice messages for all users ([#1669](https://github.com/element-hq/element-x-android/issues/1669)) + - Receive and play a voice message ([#2084](https://github.com/element-hq/element-x-android/issues/2084)) - Enable Element Call integration in rooms by default, fix several issues when creating or joining calls. Bugfixes 🐛 ---------- - - Group fallback notification to avoid having plenty of them displayed. ([#994](https://github.com/vector-im/element-x-android/issues/994)) - - Hide keyboard when exiting the chat room screen. ([#1375](https://github.com/vector-im/element-x-android/issues/1375)) - - Always register the pusher when application starts ([#1481](https://github.com/vector-im/element-x-android/issues/1481)) - - Ensure screen does not turn off when playing a video ([#1519](https://github.com/vector-im/element-x-android/issues/1519)) - - Fix issue where text is cleared when cancelling a reply ([#1617](https://github.com/vector-im/element-x-android/issues/1617)) + - Group fallback notification to avoid having plenty of them displayed. ([#994](https://github.com/element-hq/element-x-android/issues/994)) + - Hide keyboard when exiting the chat room screen. ([#1375](https://github.com/element-hq/element-x-android/issues/1375)) + - Always register the pusher when application starts ([#1481](https://github.com/element-hq/element-x-android/issues/1481)) + - Ensure screen does not turn off when playing a video ([#1519](https://github.com/element-hq/element-x-android/issues/1519)) + - Fix issue where text is cleared when cancelling a reply ([#1617](https://github.com/element-hq/element-x-android/issues/1617)) Other changes ------------- - - Remove usage of blocking methods. ([#1563](https://github.com/vector-im/element-x-android/issues/1563)) + - Remove usage of blocking methods. ([#1563](https://github.com/element-hq/element-x-android/issues/1563)) Changes in Element X v0.2.4 (2023-10-12) @@ -70,20 +121,20 @@ Changes in Element X v0.2.4 (2023-10-12) Features ✨ ---------- - - [Rich text editor] Add full screen mode ([#1447](https://github.com/vector-im/element-x-android/issues/1447)) - - Improve rendering of m.emote. ([#1497](https://github.com/vector-im/element-x-android/issues/1497)) - - Improve deleted session behavior. ([#1520](https://github.com/vector-im/element-x-android/issues/1520)) + - [Rich text editor] Add full screen mode ([#1447](https://github.com/element-hq/element-x-android/issues/1447)) + - Improve rendering of m.emote. ([#1497](https://github.com/element-hq/element-x-android/issues/1497)) + - Improve deleted session behavior. ([#1520](https://github.com/element-hq/element-x-android/issues/1520)) Bugfixes 🐛 ---------- - - WebP images can't be sent as media. ([#1483](https://github.com/vector-im/element-x-android/issues/1483)) - - Fix back button not working in bottom sheets. ([#1517](https://github.com/vector-im/element-x-android/issues/1517)) - - Render body of unknown msgtype in the timeline and in the room list ([#1539](https://github.com/vector-im/element-x-android/issues/1539)) + - WebP images can't be sent as media. ([#1483](https://github.com/element-hq/element-x-android/issues/1483)) + - Fix back button not working in bottom sheets. ([#1517](https://github.com/element-hq/element-x-android/issues/1517)) + - Render body of unknown msgtype in the timeline and in the room list ([#1539](https://github.com/element-hq/element-x-android/issues/1539)) Other changes ------------- - - Room : makes subscribeToSync/unsubscribeFromSync suspendable. ([#1457](https://github.com/vector-im/element-x-android/issues/1457)) - - Add some Konsist tests. ([#1526](https://github.com/vector-im/element-x-android/issues/1526)) + - Room : makes subscribeToSync/unsubscribeFromSync suspendable. ([#1457](https://github.com/element-hq/element-x-android/issues/1457)) + - Add some Konsist tests. ([#1526](https://github.com/element-hq/element-x-android/issues/1526)) Changes in Element X v0.2.3 (2023-09-27) @@ -91,12 +142,12 @@ Changes in Element X v0.2.3 (2023-09-27) Features ✨ ---------- - - Handle installation of Apks from the media viewer. ([#1432](https://github.com/vector-im/element-x-android/pull/1432)) - - Integrate SDK 0.1.58 ([#1437](https://github.com/vector-im/element-x-android/pull/1437)) + - Handle installation of Apks from the media viewer. ([#1432](https://github.com/element-hq/element-x-android/pull/1432)) + - Integrate SDK 0.1.58 ([#1437](https://github.com/element-hq/element-x-android/pull/1437)) Other changes ------------- - - Element call: add custom parameters to Element Call urls. ([#1434](https://github.com/vector-im/element-x-android/issues/1434)) + - Element call: add custom parameters to Element Call urls. ([#1434](https://github.com/element-hq/element-x-android/issues/1434)) Changes in Element X v0.2.2 (2023-09-21) @@ -104,8 +155,8 @@ Changes in Element X v0.2.2 (2023-09-21) Bugfixes 🐛 ---------- - - Add animation when rendering the timeline to avoid glitches. ([#1323](https://github.com/vector-im/element-x-android/issues/1323)) - - Fix crash when trying to take a photo or record a video. ([#1395](https://github.com/vector-im/element-x-android/issues/1395)) + - Add animation when rendering the timeline to avoid glitches. ([#1323](https://github.com/element-hq/element-x-android/issues/1323)) + - Fix crash when trying to take a photo or record a video. ([#1395](https://github.com/element-hq/element-x-android/issues/1395)) Changes in Element X v0.2.1 (2023-09-20) @@ -114,18 +165,18 @@ Changes in Element X v0.2.1 (2023-09-20) Features ✨ ---------- - Bump Rust SDK to `v0.1.56` - - [Rich text editor] Add link support to rich text editor ([#1309](https://github.com/vector-im/element-x-android/issues/1309)) - - Let the SDK figure the best scheme given an homeserver URL (thus allowing HTTP homeservers) ([#1382](https://github.com/vector-im/element-x-android/issues/1382)) + - [Rich text editor] Add link support to rich text editor ([#1309](https://github.com/element-hq/element-x-android/issues/1309)) + - Let the SDK figure the best scheme given an homeserver URL (thus allowing HTTP homeservers) ([#1382](https://github.com/element-hq/element-x-android/issues/1382)) Bugfixes 🐛 ---------- - - Fix ANR on RoomList when notification settings change. ([#1370](https://github.com/vector-im/element-x-android/issues/1370)) + - Fix ANR on RoomList when notification settings change. ([#1370](https://github.com/element-hq/element-x-android/issues/1370)) Other changes ------------- - - Element Call: support scheme `io.element.call` ([#1377](https://github.com/vector-im/element-x-android/issues/1377)) - - [DI] Rework how dagger components are created and provided. ([#1378](https://github.com/vector-im/element-x-android/issues/1378)) - - Remove usage of async-uniffi as it leads to a deadlocks and memory leaks. ([#1381](https://github.com/vector-im/element-x-android/issues/1381)) + - Element Call: support scheme `io.element.call` ([#1377](https://github.com/element-hq/element-x-android/issues/1377)) + - [DI] Rework how dagger components are created and provided. ([#1378](https://github.com/element-hq/element-x-android/issues/1378)) + - Remove usage of async-uniffi as it leads to a deadlocks and memory leaks. ([#1381](https://github.com/element-hq/element-x-android/issues/1381)) Changes in Element X v0.2.0 (2023-09-18) @@ -134,37 +185,37 @@ Changes in Element X v0.2.0 (2023-09-18) Features ✨ ---------- - Bump Rust SDK to `v0.1.54` - - Add a "Mute" shortcut icon and a "Notifications" section in the room details screen ([#506](https://github.com/vector-im/element-x-android/issues/506)) - - Add a notification permission screen to the initial flow. ([#897](https://github.com/vector-im/element-x-android/issues/897)) - - Integrate Element Call into EX by embedding a call in a WebView. ([#1300](https://github.com/vector-im/element-x-android/issues/1300)) - - Implement Bloom effect modifier. ([#1217](https://github.com/vector-im/element-x-android/issues/1217)) - - Set color on display name and default avatar in the timeline. ([#1224](https://github.com/vector-im/element-x-android/issues/1224)) - - Display a thread decorator in timeline so we know when a message is coming from a thread. ([#1236](https://github.com/vector-im/element-x-android/issues/1236)) - - [Rich text editor] Integrate rich text editor library. Note that markdown is now not supported and further formatting support will be introduced through the rich text editor. ([#1172](https://github.com/vector-im/element-x-android/issues/1172)) - - [Rich text editor] Add formatting menu (accessible via the '+' button) ([#1261](https://github.com/vector-im/element-x-android/issues/1261)) - - [Rich text editor] Add feature flag for rich text editor. Markdown support can now be enabled by disabling the rich text editor. ([#1289](https://github.com/vector-im/element-x-android/issues/1289)) - - [Rich text editor] Update design ([#1332](https://github.com/vector-im/element-x-android/issues/1332)) + - Add a "Mute" shortcut icon and a "Notifications" section in the room details screen ([#506](https://github.com/element-hq/element-x-android/issues/506)) + - Add a notification permission screen to the initial flow. ([#897](https://github.com/element-hq/element-x-android/issues/897)) + - Integrate Element Call into EX by embedding a call in a WebView. ([#1300](https://github.com/element-hq/element-x-android/issues/1300)) + - Implement Bloom effect modifier. ([#1217](https://github.com/element-hq/element-x-android/issues/1217)) + - Set color on display name and default avatar in the timeline. ([#1224](https://github.com/element-hq/element-x-android/issues/1224)) + - Display a thread decorator in timeline so we know when a message is coming from a thread. ([#1236](https://github.com/element-hq/element-x-android/issues/1236)) + - [Rich text editor] Integrate rich text editor library. Note that markdown is now not supported and further formatting support will be introduced through the rich text editor. ([#1172](https://github.com/element-hq/element-x-android/issues/1172)) + - [Rich text editor] Add formatting menu (accessible via the '+' button) ([#1261](https://github.com/element-hq/element-x-android/issues/1261)) + - [Rich text editor] Add feature flag for rich text editor. Markdown support can now be enabled by disabling the rich text editor. ([#1289](https://github.com/element-hq/element-x-android/issues/1289)) + - [Rich text editor] Update design ([#1332](https://github.com/element-hq/element-x-android/issues/1332)) Bugfixes 🐛 ---------- - - Make links in room topic clickable ([#612](https://github.com/vector-im/element-x-android/issues/612)) - - Reply action: harmonize conditions in bottom sheet and swipe to reply. ([#1173](https://github.com/vector-im/element-x-android/issues/1173)) - - Fix system bar color after login on light theme. ([#1222](https://github.com/vector-im/element-x-android/issues/1222)) - - Fix long click on simple formatted messages ([#1232](https://github.com/vector-im/element-x-android/issues/1232)) - - Enable polls in release build. ([#1241](https://github.com/vector-im/element-x-android/issues/1241)) - - Fix top padding in room list when app is opened in offline mode. ([#1297](https://github.com/vector-im/element-x-android/issues/1297)) - - [Rich text editor] Fix 'text formatting' option only partially visible ([#1335](https://github.com/vector-im/element-x-android/issues/1335)) - - [Rich text editor] Ensure keyboard opens for reply and text formatting modes ([#1337](https://github.com/vector-im/element-x-android/issues/1337)) - - [Rich text editor] Fix placeholder spilling onto multiple lines ([#1347](https://github.com/vector-im/element-x-android/issues/1347)) + - Make links in room topic clickable ([#612](https://github.com/element-hq/element-x-android/issues/612)) + - Reply action: harmonize conditions in bottom sheet and swipe to reply. ([#1173](https://github.com/element-hq/element-x-android/issues/1173)) + - Fix system bar color after login on light theme. ([#1222](https://github.com/element-hq/element-x-android/issues/1222)) + - Fix long click on simple formatted messages ([#1232](https://github.com/element-hq/element-x-android/issues/1232)) + - Enable polls in release build. ([#1241](https://github.com/element-hq/element-x-android/issues/1241)) + - Fix top padding in room list when app is opened in offline mode. ([#1297](https://github.com/element-hq/element-x-android/issues/1297)) + - [Rich text editor] Fix 'text formatting' option only partially visible ([#1335](https://github.com/element-hq/element-x-android/issues/1335)) + - [Rich text editor] Ensure keyboard opens for reply and text formatting modes ([#1337](https://github.com/element-hq/element-x-android/issues/1337)) + - [Rich text editor] Fix placeholder spilling onto multiple lines ([#1347](https://github.com/element-hq/element-x-android/issues/1347)) Other changes ------------- - - Add a sub-screen "Notifications" in the existing application Settings ([#510](https://github.com/vector-im/element-x-android/issues/510)) - - Exclude some groups related to analytics to be included. ([#1191](https://github.com/vector-im/element-x-android/issues/1191)) - - Use the new SyncIndicator API. ([#1244](https://github.com/vector-im/element-x-android/issues/1244)) - - Improve RoomSummary mapping by using RoomInfo. ([#1251](https://github.com/vector-im/element-x-android/issues/1251)) - - Ensure Posthog data are sent to "https://posthog.element.io" ([#1269](https://github.com/vector-im/element-x-android/issues/1269)) - - New app icon, with monochrome support. ([#1363](https://github.com/vector-im/element-x-android/issues/1363)) + - Add a sub-screen "Notifications" in the existing application Settings ([#510](https://github.com/element-hq/element-x-android/issues/510)) + - Exclude some groups related to analytics to be included. ([#1191](https://github.com/element-hq/element-x-android/issues/1191)) + - Use the new SyncIndicator API. ([#1244](https://github.com/element-hq/element-x-android/issues/1244)) + - Improve RoomSummary mapping by using RoomInfo. ([#1251](https://github.com/element-hq/element-x-android/issues/1251)) + - Ensure Posthog data are sent to "https://posthog.element.io" ([#1269](https://github.com/element-hq/element-x-android/issues/1269)) + - New app icon, with monochrome support. ([#1363](https://github.com/element-hq/element-x-android/issues/1363)) Changes in Element X v0.1.6 (2023-09-04) @@ -172,22 +223,22 @@ Changes in Element X v0.1.6 (2023-09-04) Features ✨ ---------- - - Enable the Polls feature. Allows to create, view, vote and end polls. ([#1196](https://github.com/vector-im/element-x-android/issues/1196)) -- Create poll. ([#1143](https://github.com/vector-im/element-x-android/issues/1143)) + - Enable the Polls feature. Allows to create, view, vote and end polls. ([#1196](https://github.com/element-hq/element-x-android/issues/1196)) +- Create poll. ([#1143](https://github.com/element-hq/element-x-android/issues/1143)) Bugfixes 🐛 ---------- -- Ensure notification for Event from encrypted room get decrypted content. ([#1178](https://github.com/vector-im/element-x-android/issues/1178)) - - Make sure Snackbars are only displayed once. ([#928](https://github.com/vector-im/element-x-android/issues/928)) - - Fix the orientation of sent images. ([#1135](https://github.com/vector-im/element-x-android/issues/1135)) - - Bug reporter crashes when 'send logs' is disabled. ([#1168](https://github.com/vector-im/element-x-android/issues/1168)) - - Add missing link to the terms on the analytics setting screen. ([#1177](https://github.com/vector-im/element-x-android/issues/1177)) - - Re-enable `SyncService.withEncryptionSync` to improve decryption of notifications. ([#1198](https://github.com/vector-im/element-x-android/issues/1198)) - - Crash with `aspectRatio` modifier when `Float.NaN` was used as input. ([#1995](https://github.com/vector-im/element-x-android/issues/1995)) +- Ensure notification for Event from encrypted room get decrypted content. ([#1178](https://github.com/element-hq/element-x-android/issues/1178)) + - Make sure Snackbars are only displayed once. ([#928](https://github.com/element-hq/element-x-android/issues/928)) + - Fix the orientation of sent images. ([#1135](https://github.com/element-hq/element-x-android/issues/1135)) + - Bug reporter crashes when 'send logs' is disabled. ([#1168](https://github.com/element-hq/element-x-android/issues/1168)) + - Add missing link to the terms on the analytics setting screen. ([#1177](https://github.com/element-hq/element-x-android/issues/1177)) + - Re-enable `SyncService.withEncryptionSync` to improve decryption of notifications. ([#1198](https://github.com/element-hq/element-x-android/issues/1198)) + - Crash with `aspectRatio` modifier when `Float.NaN` was used as input. ([#1995](https://github.com/element-hq/element-x-android/issues/1995)) Other changes ------------- - - Remove unnecessary year in copyright mention. ([#1187](https://github.com/vector-im/element-x-android/issues/1187)) + - Remove unnecessary year in copyright mention. ([#1187](https://github.com/element-hq/element-x-android/issues/1187)) Changes in Element X v0.1.5 (2023-08-28) @@ -195,7 +246,7 @@ Changes in Element X v0.1.5 (2023-08-28) Bugfixes 🐛 ---------- - - Fix crash when opening any room. ([#1160](https://github.com/vector-im/element-x-android/issues/1160)) + - Fix crash when opening any room. ([#1160](https://github.com/element-hq/element-x-android/issues/1160)) Changes in Element X v0.1.4 (2023-08-28) @@ -203,32 +254,32 @@ Changes in Element X v0.1.4 (2023-08-28) Features ✨ ---------- - - Allow cancelling media upload ([#769](https://github.com/vector-im/element-x-android/issues/769)) - - Enable OIDC support. ([#1127](https://github.com/vector-im/element-x-android/issues/1127)) - - Add a "Setting up account" screen, displayed the first time the user logs in to the app (per account). ([#1149](https://github.com/vector-im/element-x-android/issues/1149)) + - Allow cancelling media upload ([#769](https://github.com/element-hq/element-x-android/issues/769)) + - Enable OIDC support. ([#1127](https://github.com/element-hq/element-x-android/issues/1127)) + - Add a "Setting up account" screen, displayed the first time the user logs in to the app (per account). ([#1149](https://github.com/element-hq/element-x-android/issues/1149)) Bugfixes 🐛 ---------- - - Videos sent from the app were cropped in some cases. ([#862](https://github.com/vector-im/element-x-android/issues/862)) - - Timeline: sender names are now displayed in one single line. ([#1033](https://github.com/vector-im/element-x-android/issues/1033)) - - Fix `TextButtons` being displayed in black. ([#1077](https://github.com/vector-im/element-x-android/issues/1077)) - - Linkify links in HTML contents. ([#1079](https://github.com/vector-im/element-x-android/issues/1079)) - - Fix bug reporter failing after not finding some log files. ([#1082](https://github.com/vector-im/element-x-android/issues/1082)) - - Fix rendering of inline elements in list items. ([#1090](https://github.com/vector-im/element-x-android/issues/1090)) - - Fix crash RuntimeException "No matching key found for the ciphertext in the stream" ([#1101](https://github.com/vector-im/element-x-android/issues/1101)) - - Make links in messages clickable again. ([#1111](https://github.com/vector-im/element-x-android/issues/1111)) - - When event has no id, just cancel parsing the latest room message for a room. ([#1125](https://github.com/vector-im/element-x-android/issues/1125)) - - Only display verification prompt after initial sync is done. ([#1131](https://github.com/vector-im/element-x-android/issues/1131)) + - Videos sent from the app were cropped in some cases. ([#862](https://github.com/element-hq/element-x-android/issues/862)) + - Timeline: sender names are now displayed in one single line. ([#1033](https://github.com/element-hq/element-x-android/issues/1033)) + - Fix `TextButtons` being displayed in black. ([#1077](https://github.com/element-hq/element-x-android/issues/1077)) + - Linkify links in HTML contents. ([#1079](https://github.com/element-hq/element-x-android/issues/1079)) + - Fix bug reporter failing after not finding some log files. ([#1082](https://github.com/element-hq/element-x-android/issues/1082)) + - Fix rendering of inline elements in list items. ([#1090](https://github.com/element-hq/element-x-android/issues/1090)) + - Fix crash RuntimeException "No matching key found for the ciphertext in the stream" ([#1101](https://github.com/element-hq/element-x-android/issues/1101)) + - Make links in messages clickable again. ([#1111](https://github.com/element-hq/element-x-android/issues/1111)) + - When event has no id, just cancel parsing the latest room message for a room. ([#1125](https://github.com/element-hq/element-x-android/issues/1125)) + - Only display verification prompt after initial sync is done. ([#1131](https://github.com/element-hq/element-x-android/issues/1131)) In development 🚧 ---------------- - - [Poll] Add feature flag in developer options ([#1064](https://github.com/vector-im/element-x-android/issues/1064)) - - [Polls] Improve UI and render ended state ([#1113](https://github.com/vector-im/element-x-android/issues/1113)) + - [Poll] Add feature flag in developer options ([#1064](https://github.com/element-hq/element-x-android/issues/1064)) + - [Polls] Improve UI and render ended state ([#1113](https://github.com/element-hq/element-x-android/issues/1113)) Other changes ------------- - - Compound: add `ListItem` and `ListSectionHeader` components. ([#990](https://github.com/vector-im/element-x-android/issues/990)) - - Migrate `object` to `data object` in sealed interface / class #1135 ([#1135](https://github.com/vector-im/element-x-android/issues/1135)) + - Compound: add `ListItem` and `ListSectionHeader` components. ([#990](https://github.com/element-hq/element-x-android/issues/990)) + - Migrate `object` to `data object` in sealed interface / class #1135 ([#1135](https://github.com/element-hq/element-x-android/issues/1135)) Changes in Element X v0.1.2 (2023-08-16) @@ -236,20 +287,20 @@ Changes in Element X v0.1.2 (2023-08-16) Bugfixes 🐛 ---------- - - Filter push notifications using push rules. ([#640](https://github.com/vector-im/element-x-android/issues/640)) - - Use `for` instead of `forEach` in `DefaultDiffCacheInvalidator` to improve performance. ([#1035](https://github.com/vector-im/element-x-android/issues/1035)) + - Filter push notifications using push rules. ([#640](https://github.com/element-hq/element-x-android/issues/640)) + - Use `for` instead of `forEach` in `DefaultDiffCacheInvalidator` to improve performance. ([#1035](https://github.com/element-hq/element-x-android/issues/1035)) In development 🚧 ---------------- - - [Poll] Render start event in the timeline ([#1031](https://github.com/vector-im/element-x-android/issues/1031)) + - [Poll] Render start event in the timeline ([#1031](https://github.com/element-hq/element-x-android/issues/1031)) Other changes ------------- - - Add Button component based on Compound designs ([#1021](https://github.com/vector-im/element-x-android/issues/1021)) - - Compound: implement dialogs. ([#1043](https://github.com/vector-im/element-x-android/issues/1043)) - - Compound: customise `IconButton` component. ([#1049](https://github.com/vector-im/element-x-android/issues/1049)) - - Compound: implement `DropdownMenu` customisations. ([#1050](https://github.com/vector-im/element-x-android/issues/1050)) - - Compound: implement Snackbar component. ([#1054](https://github.com/vector-im/element-x-android/issues/1054)) + - Add Button component based on Compound designs ([#1021](https://github.com/element-hq/element-x-android/issues/1021)) + - Compound: implement dialogs. ([#1043](https://github.com/element-hq/element-x-android/issues/1043)) + - Compound: customise `IconButton` component. ([#1049](https://github.com/element-hq/element-x-android/issues/1049)) + - Compound: implement `DropdownMenu` customisations. ([#1050](https://github.com/element-hq/element-x-android/issues/1050)) + - Compound: implement Snackbar component. ([#1054](https://github.com/element-hq/element-x-android/issues/1054)) Changes in Element X v0.1.0 (2023-07-19) diff --git a/CODEOWNERS b/CODEOWNERS index 9db04197d5..40e24168c6 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @vector-im/element-x-android-reviewers +* @element-hq/element-x-android-reviewers diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3a729e7992..5cf10c0fdf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -187,7 +187,7 @@ internal fun PinIconPreview() = ElementPreview { } ``` -This will allow to preview the composable in both light and dark mode in Android Studio. This will also automatically add UI tests. The GitHub action [Record screenshots](https://github.com/vector-im/element-x-android/actions/workflows/recordScreenshots.yml) has to be run to record the new screenshots. The PR reviewer can trigger this for you if you're not part of the core team. +This will allow to preview the composable in both light and dark mode in Android Studio. This will also automatically add UI tests. The GitHub action [Record screenshots](https://github.com/element-hq/element-x-android/actions/workflows/recordScreenshots.yml) has to be run to record the new screenshots. The PR reviewer can trigger this for you if you're not part of the core team. ### Authors diff --git a/README.md b/README.md index 2a0d352187..91da420f1a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -[![Latest build](https://github.com/vector-im/element-x-android/actions/workflows/build.yml/badge.svg?query=branch%3Adevelop)](https://github.com/vector-im/element-x-android/actions/workflows/build.yml?query=branch%3Adevelop) +[![Latest build](https://github.com/element-hq/element-x-android/actions/workflows/build.yml/badge.svg?query=branch%3Adevelop)](https://github.com/element-hq/element-x-android/actions/workflows/build.yml?query=branch%3Adevelop) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vector-im_element-x-android&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android) [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=vector-im_element-x-android&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=vector-im_element-x-android&metric=bugs)](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android) -[![codecov](https://codecov.io/github/vector-im/element-x-android/branch/develop/graph/badge.svg?token=ecwvia7amV)](https://codecov.io/github/vector-im/element-x-android) +[![codecov](https://codecov.io/github/element-hq/element-x-android/branch/develop/graph/badge.svg?token=ecwvia7amV)](https://codecov.io/github/vector-im/element-x-android) [![Element X_Android Matrix room #element-x-android:matrix.org](https://img.shields.io/matrix/element-x-android:matrix.org.svg?label=%23element-x-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-x-android:matrix.org) [![Localazy](https://img.shields.io/endpoint?url=https%3A%2F%2Fconnect.localazy.com%2Fstatus%2Felement%2Fdata%3Fcontent%3Dall%26title%3Dlocalazy%26logo%3Dtrue)](https://localazy.com/p/element) @@ -10,7 +10,7 @@ Element X Android is a [Matrix](https://matrix.org/) Android Client provided by [element.io](https://element.io/). This app is currently in a pre-alpha release stage with only basic functionalities. -The application is a total rewrite of [Element-Android](https://github.com/vector-im/element-android) using the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) underneath and targeting devices running Android 6+. The UI layer is written using [Jetpack Compose](https://developer.android.com/jetpack/compose), and the navigation is managed using [Appyx](https://github.com/bumble-tech/appyx). +The application is a total rewrite of [Element-Android](https://github.com/element-hq/element-android) using the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) underneath and targeting devices running Android 6+. The UI layer is written using [Jetpack Compose](https://developer.android.com/jetpack/compose), and the navigation is managed using [Appyx](https://github.com/bumble-tech/appyx). Learn more about why we are building Element X in our blog post: [https://element.io/blog/element-x-experience-the-future-of-element/](https://element.io/blog/element-x-experience-the-future-of-element/). @@ -58,11 +58,11 @@ We're doing this as a way to share code between platforms and while we've seen p ## Status -This project is in work in progress. The app does not cover yet all functionalities we expect. The list of supported features can be found in [this issue](https://github.com/vector-im/element-x-android/issues/911). +This project is in work in progress. The app does not cover yet all functionalities we expect. The list of supported features can be found in [this issue](https://github.com/element-hq/element-x-android/issues/911). ## Contributing -Want to get actively involved in the project? You're more than welcome! A good way to start is to check the issues that are labelled with the [good first issue](https://github.com/vector-im/element-x-android/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) label. Let us know by commenting the issue that you're starting working on it. +Want to get actively involved in the project? You're more than welcome! A good way to start is to check the issues that are labelled with the [good first issue](https://github.com/element-hq/element-x-android/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) label. Let us know by commenting the issue that you're starting working on it. But first make sure to read our [contribution guide](CONTRIBUTING.md) first. @@ -75,7 +75,7 @@ Makes sure to select the `app` configuration when building (as we also have samp ## Support -When you are experiencing an issue on Element X Android, please first search in [GitHub issues](https://github.com/vector-im/element-x-android/issues) +When you are experiencing an issue on Element X Android, please first search in [GitHub issues](https://github.com/element-hq/element-x-android/issues) and then in [#element-x-android:matrix.org](https://matrix.to/#/#element-x-android:matrix.org). If after your research you still have a question, ask at [#element-x-android:matrix.org](https://matrix.to/#/#element-x-android:matrix.org). Otherwise feel free to create a GitHub issue if you encounter a bug or a crash, by explaining clearly in detail what happened. You can also perform bug reporting from the application settings. This is especially recommended when you encounter a crash. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 69a1877455..f936e991fc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -225,6 +225,7 @@ dependencies { kapt(libs.dagger.compiler) testImplementation(libs.test.junit) + testImplementation(libs.test.robolectric) testImplementation(libs.coroutines.test) testImplementation(libs.molecule.runtime) testImplementation(libs.test.truth) diff --git a/app/src/main/kotlin/io/element/android/x/MainActivity.kt b/app/src/main/kotlin/io/element/android/x/MainActivity.kt index c8e16f708e..00d8a48d9a 100644 --- a/app/src/main/kotlin/io/element/android/x/MainActivity.kt +++ b/app/src/main/kotlin/io/element/android/x/MainActivity.kt @@ -35,15 +35,15 @@ import androidx.core.view.WindowCompat import com.bumble.appyx.core.integration.NodeHost import com.bumble.appyx.core.integrationpoint.NodeActivity import com.bumble.appyx.core.plugin.NodeReadyObserver +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.theme.Theme +import io.element.android.compound.theme.isDark +import io.element.android.compound.theme.mapToTheme import io.element.android.features.lockscreen.api.handleSecureFlag import io.element.android.features.lockscreen.api.isLocked import io.element.android.libraries.architecture.bindings import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.designsystem.utils.snackbar.LocalSnackbarDispatcher -import io.element.android.libraries.theme.ElementTheme -import io.element.android.libraries.theme.theme.Theme -import io.element.android.libraries.theme.theme.isDark -import io.element.android.libraries.theme.theme.mapToTheme import io.element.android.x.di.AppBindings import io.element.android.x.intent.SafeUriHandler import timber.log.Timber diff --git a/app/src/main/kotlin/io/element/android/x/MainNode.kt b/app/src/main/kotlin/io/element/android/x/MainNode.kt index d412519d35..94cd7fa7e1 100644 --- a/app/src/main/kotlin/io/element/android/x/MainNode.kt +++ b/app/src/main/kotlin/io/element/android/x/MainNode.kt @@ -52,7 +52,7 @@ class MainNode( override val daggerComponent = (context as DaggerComponentOwner).daggerComponent override fun resolve(navTarget: RootNavTarget, buildContext: BuildContext): Node { - return createNode(context = buildContext) + return createNode(buildContext = buildContext) } @Composable diff --git a/app/src/main/kotlin/io/element/android/x/initializer/TracingInitializer.kt b/app/src/main/kotlin/io/element/android/x/initializer/TracingInitializer.kt index 853412e403..8dc0bf0e0e 100644 --- a/app/src/main/kotlin/io/element/android/x/initializer/TracingInitializer.kt +++ b/app/src/main/kotlin/io/element/android/x/initializer/TracingInitializer.kt @@ -57,6 +57,7 @@ class TracingInitializer : Initializer { ) } bugReporter.cleanLogDirectoryIfNeeded() + bugReporter.setCurrentTracingFilter(tracingConfiguration.filterConfiguration.filter) tracingService.setupTracing(tracingConfiguration) // Also set env variable for rust back trace Os.setenv("RUST_BACKTRACE", "1", true) diff --git a/app/src/main/res/xml/file_providers.xml b/app/src/main/res/xml/file_providers.xml index 8afae6f313..9192b4add3 100644 --- a/app/src/main/res/xml/file_providers.xml +++ b/app/src/main/res/xml/file_providers.xml @@ -14,6 +14,6 @@ ~ limitations under the License. --> - + diff --git a/app/src/test/kotlin/io/element/android/x/intent/IntentProviderImplTest.kt b/app/src/test/kotlin/io/element/android/x/intent/IntentProviderImplTest.kt new file mode 100644 index 0000000000..9f0cde38cf --- /dev/null +++ b/app/src/test/kotlin/io/element/android/x/intent/IntentProviderImplTest.kt @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023 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 io.element.android.x.intent + +import android.content.Context +import android.content.Intent +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.deeplink.DeepLinkCreator +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.matrix.test.A_THREAD_ID +import io.element.android.x.MainActivity +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +@RunWith(RobolectricTestRunner::class) +class IntentProviderImplTest { + + @Test + fun `test getViewRoomIntent with Session`() { + val sut = createIntentProviderImpl() + val result = sut.getViewRoomIntent( + sessionId = A_SESSION_ID, + roomId = null, + threadId = null, + ) + result.commonAssertions() + assertThat(result.data.toString()).isEqualTo("elementx://open/@alice:server.org") + } + + @Test + fun `test getViewRoomIntent with Session and Room`() { + val sut = createIntentProviderImpl() + val result = sut.getViewRoomIntent( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + threadId = null, + ) + result.commonAssertions() + assertThat(result.data.toString()).isEqualTo("elementx://open/@alice:server.org/!aRoomId:domain") + } + + @Test + fun `test getViewRoomIntent with Session, Room and Thread`() { + val sut = createIntentProviderImpl() + val result = sut.getViewRoomIntent( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + threadId = A_THREAD_ID, + ) + result.commonAssertions() + assertThat(result.data.toString()).isEqualTo("elementx://open/@alice:server.org/!aRoomId:domain/\$aThreadId") + } + + @Test + fun `test getInviteListIntent`() { + val sut = createIntentProviderImpl() + val result = sut.getInviteListIntent( + sessionId = A_SESSION_ID, + ) + result.commonAssertions() + assertThat(result.data.toString()).isEqualTo("elementx://open/@alice:server.org/invites") + } + + private fun createIntentProviderImpl(): IntentProviderImpl { + return IntentProviderImpl( + context = RuntimeEnvironment.getApplication() as Context, + deepLinkCreator = DeepLinkCreator(), + ) + } + + private fun Intent.commonAssertions() { + assertThat(action).isEqualTo(Intent.ACTION_VIEW) + assertThat(component?.className).isEqualTo(MainActivity::class.java.name) + } +} diff --git a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/Config.kt b/appconfig/src/main/kotlin/io/element/android/appconfig/AnalyticsConfig.kt similarity index 90% rename from features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/Config.kt rename to appconfig/src/main/kotlin/io/element/android/appconfig/AnalyticsConfig.kt index 883e0d1dc3..4461a9b051 100644 --- a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/Config.kt +++ b/appconfig/src/main/kotlin/io/element/android/appconfig/AnalyticsConfig.kt @@ -14,9 +14,8 @@ * limitations under the License. */ -package io.element.android.features.analytics.api +package io.element.android.appconfig -object Config { +object AnalyticsConfig { const val POLICY_LINK = "https://element.io/cookie-policy" } - diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/NotificationConfig.kt b/appconfig/src/main/kotlin/io/element/android/appconfig/NotificationConfig.kt similarity index 94% rename from libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/NotificationConfig.kt rename to appconfig/src/main/kotlin/io/element/android/appconfig/NotificationConfig.kt index eb37332f8f..30940cbcaf 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/NotificationConfig.kt +++ b/appconfig/src/main/kotlin/io/element/android/appconfig/NotificationConfig.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.push.impl +package io.element.android.appconfig object NotificationConfig { // TODO EAx Implement and set to true at some point diff --git a/appconfig/src/main/kotlin/io/element/android/appconfig/RoomListConfig.kt b/appconfig/src/main/kotlin/io/element/android/appconfig/RoomListConfig.kt new file mode 100644 index 0000000000..b464d9794c --- /dev/null +++ b/appconfig/src/main/kotlin/io/element/android/appconfig/RoomListConfig.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 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 io.element.android.appconfig + +object RoomListConfig { + const val showInviteMenuItem = false + const val showReportProblemMenuItem = false + + const val hasDropdownMenu = showInviteMenuItem || showReportProblemMenuItem +} diff --git a/appnav/build.gradle.kts b/appnav/build.gradle.kts index 88f0741ebe..9407b16592 100644 --- a/appnav/build.gradle.kts +++ b/appnav/build.gradle.kts @@ -58,12 +58,14 @@ dependencies { implementation(projects.services.analytics.api) testImplementation(libs.test.junit) + testImplementation(libs.test.robolectric) testImplementation(libs.coroutines.test) testImplementation(libs.molecule.runtime) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrix.test) testImplementation(projects.features.networkmonitor.test) + testImplementation(projects.features.login.impl) testImplementation(projects.tests.testutils) testImplementation(projects.features.rageshake.test) testImplementation(projects.features.rageshake.impl) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt index 7f36a7a51a..423228c110 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt @@ -33,13 +33,12 @@ import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.appnav.di.SessionComponentFactory import io.element.android.libraries.architecture.NodeInputs -import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.DaggerComponentOwner import io.element.android.libraries.matrix.api.MatrixClient -import io.element.android.libraries.matrix.ui.di.MatrixUIBindings +import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder import kotlinx.parcelize.Parcelize /** @@ -52,6 +51,7 @@ class LoggedInAppScopeFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, sessionComponentFactory: SessionComponentFactory, + private val imageLoaderHolder: ImageLoaderHolder, ) : ParentNode( navModel = PermanentNavModel( navTargets = setOf(NavTarget), @@ -78,8 +78,7 @@ class LoggedInAppScopeFlowNode @AssistedInject constructor( super.onBuilt() lifecycle.subscribe( onCreate = { - val imageLoaderFactory = bindings().loggedInImageLoaderFactory() - Coil.setImageLoader(imageLoaderFactory) + Coil.setImageLoader(imageLoaderHolder.get(inputs.matrixClient)) }, ) } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index 23c35b119a..884681766e 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -25,7 +25,6 @@ import androidx.compose.ui.Modifier import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle -import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.composable.PermanentChild import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext @@ -57,8 +56,8 @@ import io.element.android.features.preferences.api.PreferencesEntryPoint import io.element.android.features.roomlist.api.RoomListEntryPoint import io.element.android.features.securebackup.api.SecureBackupEntryPoint import io.element.android.features.verifysession.api.VerifySessionEntryPoint -import io.element.android.libraries.architecture.BackstackNode -import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler +import io.element.android.libraries.architecture.BackstackView +import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.waitForChildAttached import io.element.android.libraries.deeplink.DeeplinkData @@ -67,6 +66,7 @@ import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.MAIN_SPACE import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.roomlist.RoomList import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.push.api.notifications.NotificationDrawerManager import io.element.android.services.appnavstate.api.AppNavigationStateService @@ -99,7 +99,7 @@ class LoggedInFlowNode @AssistedInject constructor( private val lockScreenStateService: LockScreenService, private val matrixClient: MatrixClient, snackbarDispatcher: SnackbarDispatcher, -) : BackstackNode( +) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.RoomList, savedStateMap = buildContext.savedStateMap, @@ -149,6 +149,23 @@ class LoggedInFlowNode @AssistedInject constructor( } ) observeSyncStateAndNetworkStatus() + observeInvitesLoadingState() + } + + private fun observeInvitesLoadingState() { + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + matrixClient.roomListService.invites.loadingState + .collect { inviteState -> + when (inviteState) { + is RoomList.LoadingState.Loaded -> if (inviteState.numberOfRooms == 0) { + backstack.removeLast(NavTarget.InviteList) + } + RoomList.LoadingState.NotLoaded -> Unit + } + } + } + } } @OptIn(FlowPreview::class) @@ -256,6 +273,10 @@ class LoggedInFlowNode @AssistedInject constructor( } is NavTarget.Room -> { val callback = object : RoomLoadedFlowNode.Callback { + override fun onOpenRoom(roomId: RoomId) { + backstack.push(NavTarget.Room(roomId)) + } + override fun onForwardedToSingleRoom(roomId: RoomId) { coroutineScope.launch { attachRoom(roomId) } } @@ -362,12 +383,7 @@ class LoggedInFlowNode @AssistedInject constructor( override fun View(modifier: Modifier) { Box(modifier = modifier) { val lockScreenState by lockScreenStateService.lockState.collectAsState() - Children( - navModel = backstack, - modifier = Modifier, - // Animate navigation to settings and to a room - transitionHandler = rememberDefaultTransitionHandler(), - ) + BackstackView() val isFtueDisplayed by ftueState.shouldDisplayFlow.collectAsState() if (!isFtueDisplayed) { PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt index 6c22644658..9401cdeac1 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt @@ -20,7 +20,6 @@ import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import coil.Coil -import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node @@ -33,8 +32,8 @@ import io.element.android.anvilannotations.ContributesNode import io.element.android.features.login.api.LoginEntryPoint import io.element.android.features.onboarding.api.OnBoardingEntryPoint import io.element.android.features.preferences.api.ConfigureTracingEntryPoint -import io.element.android.libraries.architecture.BackstackNode -import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler +import io.element.android.libraries.architecture.BackstackView +import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.di.AppScope import io.element.android.libraries.matrix.ui.media.NotLoggedInImageLoaderFactory import kotlinx.parcelize.Parcelize @@ -47,7 +46,7 @@ class NotLoggedInFlowNode @AssistedInject constructor( private val configureTracingEntryPoint: ConfigureTracingEntryPoint, private val loginEntryPoint: LoginEntryPoint, private val notLoggedInImageLoaderFactory: NotLoggedInImageLoaderFactory, -) : BackstackNode( +) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.OnBoarding, savedStateMap = buildContext.savedStateMap @@ -111,11 +110,6 @@ class NotLoggedInFlowNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { - Children( - navModel = backstack, - modifier = modifier, - // Animate navigation to login screen - transitionHandler = rememberDefaultTransitionHandler(), - ) + BackstackView() } } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt index df98998ba5..c9dcd62048 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt @@ -24,7 +24,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.lifecycle.lifecycleScope -import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.node @@ -46,8 +45,8 @@ import io.element.android.features.login.api.oidc.OidcAction import io.element.android.features.login.api.oidc.OidcActionFlow import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint import io.element.android.features.signedout.api.SignedOutEntryPoint -import io.element.android.libraries.architecture.BackstackNode -import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler +import io.element.android.libraries.architecture.BackstackView +import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.waitForChildAttached import io.element.android.libraries.deeplink.DeeplinkData @@ -74,7 +73,7 @@ class RootFlowNode @AssistedInject constructor( private val signedOutEntryPoint: SignedOutEntryPoint, private val intentResolver: IntentResolver, private val oidcActionFlow: OidcActionFlow, -) : BackstackNode( +) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.SplashScreen, savedStateMap = buildContext.savedStateMap, @@ -134,8 +133,8 @@ class RootFlowNode @AssistedInject constructor( private suspend fun restoreSessionIfNeeded( sessionId: SessionId, - onFailure: () -> Unit = {}, - onSuccess: (SessionId) -> Unit = {}, + onFailure: () -> Unit, + onSuccess: (SessionId) -> Unit, ) { matrixClientsHolder.getOrRestore(sessionId) .onSuccess { @@ -149,8 +148,8 @@ class RootFlowNode @AssistedInject constructor( } private suspend fun tryToRestoreLatestSession( - onSuccess: (SessionId) -> Unit = {}, - onFailure: () -> Unit = {} + onSuccess: (SessionId) -> Unit, + onFailure: () -> Unit ) { val latestSessionId = authenticationService.getLatestSessionId() if (latestSessionId == null) { @@ -172,11 +171,7 @@ class RootFlowNode @AssistedInject constructor( modifier = modifier, onOpenBugReport = this::onOpenBugReport, ) { - Children( - navModel = backstack, - // Animate opening the bug report screen - transitionHandler = rememberDefaultTransitionHandler(), - ) + BackstackView() } } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt b/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt index b567395c1e..96febc3751 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt @@ -34,6 +34,8 @@ class IntentResolver @Inject constructor( private val oidcIntentResolver: OidcIntentResolver ) { fun resolve(intent: Intent): ResolvedIntent? { + if (intent.canBeIgnored()) return null + val deepLinkData = deeplinkParser.getFromIntent(intent) if (deepLinkData != null) return ResolvedIntent.Navigation(deepLinkData) @@ -45,3 +47,8 @@ class IntentResolver @Inject constructor( return null } } + +private fun Intent.canBeIgnored(): Boolean { + return action == Intent.ACTION_MAIN && + categories?.contains(Intent.CATEGORY_LAUNCHER) == true +} diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt index cadcef338d..be9550d990 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt @@ -25,8 +25,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight @Composable fun LoggedInView( diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt index d82ef72145..43b770f327 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt @@ -33,12 +33,12 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @Composable diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt index 1f8d9cfebb..69a5e9c8a2 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt @@ -30,17 +30,17 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.button.BackButton -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @OptIn(ExperimentalLayoutApi::class) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt index c3df0f0ee2..8085494483 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt @@ -25,7 +25,6 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.lifecycle.lifecycleScope -import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.node @@ -38,7 +37,8 @@ import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkStatus -import io.element.android.libraries.architecture.BackstackNode +import io.element.android.libraries.architecture.BackstackView +import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs @@ -57,7 +57,7 @@ class RoomFlowNode @AssistedInject constructor( loadingRoomStateFlowFactory: LoadingRoomStateFlowFactory, private val networkMonitor: NetworkMonitor, ) : - BackstackNode( + BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Loading, savedStateMap = buildContext.savedStateMap, @@ -130,10 +130,7 @@ class RoomFlowNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { - Children( - navModel = backstack, - modifier = modifier, - ) + BackstackView() } } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt index 613ed650c8..045823be6f 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt @@ -22,7 +22,6 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.ui.Modifier import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope -import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node @@ -35,9 +34,9 @@ import io.element.android.anvilannotations.ContributesNode import io.element.android.appnav.di.RoomComponentFactory import io.element.android.features.messages.api.MessagesEntryPoint import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint -import io.element.android.libraries.architecture.BackstackNode +import io.element.android.libraries.architecture.BackstackView +import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.NodeInputs -import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.DaggerComponentOwner import io.element.android.libraries.di.SessionScope @@ -64,7 +63,7 @@ class RoomLoadedFlowNode @AssistedInject constructor( private val appCoroutineScope: CoroutineScope, roomComponentFactory: RoomComponentFactory, roomMembershipObserver: RoomMembershipObserver, -) : BackstackNode( +) : BaseFlowNode( backstack = BackStack( initialElement = plugins.filterIsInstance(Inputs::class.java).first().initialElement, savedStateMap = buildContext.savedStateMap, @@ -74,6 +73,7 @@ class RoomLoadedFlowNode @AssistedInject constructor( ), DaggerComponentOwner { interface Callback : Plugin { + fun onOpenRoom(roomId: RoomId) fun onForwardedToSingleRoom(roomId: RoomId) fun onOpenGlobalNotificationSettings() } @@ -134,6 +134,10 @@ class RoomLoadedFlowNode @AssistedInject constructor( override fun onOpenGlobalNotificationSettings() { callbacks.forEach { it.onOpenGlobalNotificationSettings() } } + + override fun onOpenRoom(roomId: RoomId) { + callbacks.forEach { it.onOpenRoom(roomId) } + } } return roomDetailsEntryPoint.nodeBuilder(this, buildContext) .params(RoomDetailsEntryPoint.Params(initialTarget)) @@ -197,10 +201,6 @@ class RoomLoadedFlowNode @AssistedInject constructor( } } } - Children( - navModel = backstack, - modifier = modifier, - transitionHandler = rememberDefaultTransitionHandler(), - ) + BackstackView() } } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/root/RootView.kt b/appnav/src/main/kotlin/io/element/android/appnav/root/RootView.kt index 53eae2395e..26772e56fa 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/root/RootView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/root/RootView.kt @@ -27,16 +27,16 @@ import io.element.android.features.rageshake.api.crash.CrashDetectionEvents import io.element.android.features.rageshake.api.crash.CrashDetectionView import io.element.android.features.rageshake.api.detection.RageshakeDetectionEvents import io.element.android.features.rageshake.api.detection.RageshakeDetectionView -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.services.apperror.impl.AppErrorView @Composable fun RootView( state: RootState, + onOpenBugReport: () -> Unit, modifier: Modifier = Modifier, - onOpenBugReport: () -> Unit = {}, children: @Composable BoxScope.() -> Unit, ) { Box( @@ -69,7 +69,10 @@ fun RootView( @PreviewsDayNight @Composable internal fun RootPreview(@PreviewParameter(RootStateProvider::class) rootState: RootState) = ElementPreview { - RootView(rootState) { + RootView( + state = rootState, + onOpenBugReport = {}, + ) { Text("Children") } } diff --git a/appnav/src/test/kotlin/io/element/android/appnav/RoomFlowNodeTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/RoomFlowNodeTest.kt index fd5de85b1d..09ca595080 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/RoomFlowNodeTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/RoomFlowNodeTest.kt @@ -25,7 +25,7 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.navmodel.backstack.activeElement import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.bumble.appyx.testing.unit.common.helper.parentNodeTestHelper -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import io.element.android.appnav.di.RoomComponentFactory import io.element.android.appnav.room.RoomLoadedFlowNode import io.element.android.features.messages.api.MessagesEntryPoint @@ -83,7 +83,7 @@ class RoomFlowNodeTest { } override fun build(): Node { - return node(buildContext) {}.also { + return node(buildContext) {}.also { nodeId = it.id } } @@ -122,10 +122,10 @@ class RoomFlowNodeTest { val roomFlowNodeTestHelper = roomFlowNode.parentNodeTestHelper() // THEN - Truth.assertThat(roomFlowNode.backstack.activeElement).isEqualTo(RoomLoadedFlowNode.NavTarget.Messages) + assertThat(roomFlowNode.backstack.activeElement).isEqualTo(RoomLoadedFlowNode.NavTarget.Messages) roomFlowNodeTestHelper.assertChildHasLifecycle(RoomLoadedFlowNode.NavTarget.Messages, Lifecycle.State.CREATED) val messagesNode = roomFlowNode.childNode(RoomLoadedFlowNode.NavTarget.Messages)!! - Truth.assertThat(messagesNode.id).isEqualTo(fakeMessagesEntryPoint.nodeId) + assertThat(messagesNode.id).isEqualTo(fakeMessagesEntryPoint.nodeId) } @Test @@ -147,6 +147,6 @@ class RoomFlowNodeTest { // THEN roomFlowNodeTestHelper.assertChildHasLifecycle(RoomLoadedFlowNode.NavTarget.RoomDetails, Lifecycle.State.CREATED) val roomDetailsNode = roomFlowNode.childNode(RoomLoadedFlowNode.NavTarget.RoomDetails)!! - Truth.assertThat(roomDetailsNode.id).isEqualTo(fakeRoomDetailsEntryPoint.nodeId) + assertThat(roomDetailsNode.id).isEqualTo(fakeRoomDetailsEntryPoint.nodeId) } } diff --git a/appnav/src/test/kotlin/io/element/android/appnav/intent/IntentResolverTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/intent/IntentResolverTest.kt new file mode 100644 index 0000000000..3ad11787df --- /dev/null +++ b/appnav/src/test/kotlin/io/element/android/appnav/intent/IntentResolverTest.kt @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2023 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 io.element.android.appnav.intent + +import android.app.Activity +import android.content.Intent +import androidx.core.net.toUri +import com.google.common.truth.Truth.assertThat +import io.element.android.features.login.api.oidc.OidcAction +import io.element.android.features.login.impl.oidc.DefaultOidcIntentResolver +import io.element.android.features.login.impl.oidc.OidcUrlParser +import io.element.android.libraries.deeplink.DeepLinkCreator +import io.element.android.libraries.deeplink.DeeplinkData +import io.element.android.libraries.deeplink.DeeplinkParser +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.matrix.test.A_THREAD_ID +import org.junit.Assert.assertThrows +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +@RunWith(RobolectricTestRunner::class) +class IntentResolverTest { + @Test + fun `resolve launcher intent should return null`() { + val sut = createIntentResolver() + val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { + action = Intent.ACTION_MAIN + addCategory(Intent.CATEGORY_LAUNCHER) + } + val result = sut.resolve(intent) + assertThat(result).isNull() + } + + @Test + fun `test resolve navigation intent root`() { + val sut = createIntentResolver() + val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { + action = Intent.ACTION_VIEW + data = DeepLinkCreator().room( + sessionId = A_SESSION_ID, + roomId = null, + threadId = null, + ) + .toUri() + } + val result = sut.resolve(intent) + assertThat(result).isEqualTo( + ResolvedIntent.Navigation( + deeplinkData = DeeplinkData.Root( + sessionId = A_SESSION_ID, + ) + ) + ) + } + + @Test + fun `test resolve navigation intent room`() { + val sut = createIntentResolver() + val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { + action = Intent.ACTION_VIEW + data = DeepLinkCreator().room( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + threadId = null, + ) + .toUri() + } + val result = sut.resolve(intent) + assertThat(result).isEqualTo( + ResolvedIntent.Navigation( + deeplinkData = DeeplinkData.Room( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + threadId = null, + ) + ) + ) + } + + @Test + fun `test resolve navigation intent thread`() { + val sut = createIntentResolver() + val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { + action = Intent.ACTION_VIEW + data = DeepLinkCreator().room( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + threadId = A_THREAD_ID, + ) + .toUri() + } + val result = sut.resolve(intent) + assertThat(result).isEqualTo( + ResolvedIntent.Navigation( + deeplinkData = DeeplinkData.Room( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + threadId = A_THREAD_ID, + ) + ) + ) + } + + @Test + fun `test resolve oidc go back`() { + val sut = createIntentResolver() + val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { + action = Intent.ACTION_VIEW + data = "io.element:/callback?error=access_denied&state=IFF1UETGye2ZA8pO".toUri() + } + val result = sut.resolve(intent) + assertThat(result).isEqualTo( + ResolvedIntent.Oidc( + oidcAction = OidcAction.GoBack + ) + ) + } + + @Test + fun `test resolve oidc success`() { + val sut = createIntentResolver() + val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { + action = Intent.ACTION_VIEW + data = "io.element:/callback?state=IFF1UETGye2ZA8pO&code=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB".toUri() + } + val result = sut.resolve(intent) + assertThat(result).isEqualTo( + ResolvedIntent.Oidc( + oidcAction = OidcAction.Success( + url = "io.element:/callback?state=IFF1UETGye2ZA8pO&code=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB" + ) + ) + ) + } + + @Test + fun `test resolve oidc invalid`() { + val sut = createIntentResolver() + val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { + action = Intent.ACTION_VIEW + data = "io.element:/callback/invalid".toUri() + } + assertThrows(IllegalStateException::class.java) { + sut.resolve(intent) + } + } + + @Test + fun `test resolve invalid`() { + val sut = createIntentResolver() + val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { + action = Intent.ACTION_VIEW + data = "io.element:/invalid".toUri() + } + val result = sut.resolve(intent) + assertThat(result).isNull() + } + + private fun createIntentResolver(): IntentResolver { + return IntentResolver( + deeplinkParser = DeeplinkParser(), + oidcIntentResolver = DefaultOidcIntentResolver( + oidcUrlParser = OidcUrlParser() + ), + ) + } +} diff --git a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt index fcfba8f7a1..d7b951fbbc 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt @@ -60,8 +60,9 @@ class LoggedInPresenterTest { }.test { val initialState = awaitItem() assertThat(initialState.showSyncSpinner).isFalse() + roomListService.postSyncIndicator(RoomListService.SyncIndicator.Show) consumeItemsUntilPredicate { it.showSyncSpinner } - roomListService.postState(RoomListService.State.Running) + roomListService.postSyncIndicator(RoomListService.SyncIndicator.Hide) consumeItemsUntilPredicate { !it.showSyncSpinner } } } diff --git a/appnav/src/test/kotlin/io/element/android/appnav/room/LoadingRoomStateFlowFactoryTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/room/LoadingRoomStateFlowFactoryTest.kt index f56367e5f8..6997f07233 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/room/LoadingRoomStateFlowFactoryTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/room/LoadingRoomStateFlowFactoryTest.kt @@ -17,7 +17,7 @@ package io.element.android.appnav.room import app.cash.turbine.test -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.roomlist.RoomList import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID @@ -39,8 +39,8 @@ class LoadingRoomStateFlowFactoryTest { flowFactory .create(this, A_ROOM_ID) .test { - Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loading) - Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loaded(room)) + assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loading) + assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loaded(room)) } } @@ -53,10 +53,10 @@ class LoadingRoomStateFlowFactoryTest { flowFactory .create(this, A_ROOM_ID) .test { - Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loading) + assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loading) matrixClient.givenGetRoomResult(A_ROOM_ID, room) roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1)) - Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loaded(room)) + assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loaded(room)) } } @@ -68,12 +68,9 @@ class LoadingRoomStateFlowFactoryTest { flowFactory .create(this, A_ROOM_ID) .test { - Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loading) + assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loading) roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1)) - Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Error) + assertThat(awaitItem()).isEqualTo(LoadingRoomState.Error) } } - - - } diff --git a/build.gradle.kts b/build.gradle.kts index a2dbec823c..1a19007a28 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -62,7 +62,7 @@ allprojects { config.from(files("$rootDir/tools/detekt/detekt.yml")) } dependencies { - detektPlugins("io.nlopez.compose.rules:detekt:0.3.3") + detektPlugins("io.nlopez.compose.rules:detekt:0.3.8") } // KtLint @@ -130,10 +130,10 @@ sonar { property("sonar.host.url", "https://sonarcloud.io") property("sonar.projectVersion", "1.0") // TODO project(":app").android.defaultConfig.versionName) property("sonar.sourceEncoding", "UTF-8") - property("sonar.links.homepage", "https://github.com/vector-im/element-x-android/") - property("sonar.links.ci", "https://github.com/vector-im/element-x-android/actions") - property("sonar.links.scm", "https://github.com/vector-im/element-x-android/") - property("sonar.links.issue", "https://github.com/vector-im/element-x-android/issues") + property("sonar.links.homepage", "https://github.com/element-hq/element-x-android/") + property("sonar.links.ci", "https://github.com/element-hq/element-x-android/actions") + property("sonar.links.scm", "https://github.com/element-hq/element-x-android/") + property("sonar.links.issue", "https://github.com/element-hq/element-x-android/issues") property("sonar.organization", "new_vector_ltd_organization") property("sonar.login", if (project.hasProperty("SONAR_LOGIN")) project.property("SONAR_LOGIN")!! else "invalid") @@ -236,11 +236,11 @@ koverMerged { name = "Global minimum code coverage." target = kotlinx.kover.api.VerificationTarget.ALL bound { - minValue = 60 + minValue = 65 // Setting a max value, so that if coverage is bigger, it means that we have to change minValue. // For instance if we have minValue = 20 and maxValue = 30, and current code coverage is now 31.32%, update // minValue to 25 and maxValue to 35. - maxValue = 70 + maxValue = 75 counter = kotlinx.kover.api.CounterType.INSTRUCTION valueType = kotlinx.kover.api.VerificationValueType.COVERED_PERCENTAGE } @@ -376,3 +376,22 @@ subprojects { } } } + +subprojects { + tasks.withType().configureEach { + kotlinOptions { + if (project.findProperty("composeCompilerReports") == "true") { + freeCompilerArgs += listOf( + "-P", + "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=${project.layout.buildDirectory.asFile.get().absolutePath}/compose_compiler" + ) + } + if (project.findProperty("composeCompilerMetrics") == "true") { + freeCompilerArgs += listOf( + "-P", + "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=${project.layout.buildDirectory.asFile.get().absolutePath}/compose_compiler" + ) + } + } + } +} diff --git a/docs/_developer_onboarding.md b/docs/_developer_onboarding.md index a100a2beba..b4ea411710 100644 --- a/docs/_developer_onboarding.md +++ b/docs/_developer_onboarding.md @@ -117,7 +117,7 @@ You can also have access to the aars through the [release](https://github.com/ma #### Build the SDK locally -Easiest way: run the script [./tools/sdk/build_rust_sdk.sh](./tools/sdk/build_rust_sdk.sh) and just answer the questions. +Easiest way: run the script [../tools/sdk/build_rust_sdk.sh](../tools/sdk/build_rust_sdk.sh) and just answer the questions. Legacy way: @@ -322,7 +322,7 @@ We are using [Gradle version catalog](https://docs.gradle.org/current/userguide/ All the dependencies (including android artifact, gradle plugin, etc.) should be declared in [../gradle/libs.versions.toml](libs.versions.toml) file. Some dependency, mainly because they are not shared can be declared in `build.gradle.kts` files. -[Renovate](https://github.com/apps/renovate) is set up on the project. This tool will automatically create Pull Request to upgrade our dependencies one by one. A [dependency dashboard issue](https://github.com/vector-im/element-x-android/issues/150) is maintained by the tool and allow to perform some actions. +[Renovate](https://github.com/apps/renovate) is set up on the project. This tool will automatically create Pull Request to upgrade our dependencies one by one. A [dependency dashboard issue](https://github.com/element-hq/element-x-android/issues/150) is maintained by the tool and allow to perform some actions. ### Test diff --git a/docs/danger.md b/docs/danger.md index e6fa74dec2..5cf34ac055 100644 --- a/docs/danger.md +++ b/docs/danger.md @@ -63,7 +63,7 @@ bundle exec danger pr --dangerfile=./tools/danger/dangerfile.js For instance: ```shell -bundle exec danger pr https://github.com/vector-im/element-android/pull/6637 --dangerfile=./tools/danger/dangerfile.js +bundle exec danger pr https://github.com/element-hq/element-android/pull/6637 --dangerfile=./tools/danger/dangerfile.js ``` We may need to create a GitHub token to have less API rate limiting, and then set the env var: @@ -84,7 +84,7 @@ bundle exec danger-kotlin pr --dangerfile=./tools/danger/dangerfile.js To let Danger check all the PRs, including PRs form forks, a GitHub account have been created: - login: ElementBot - password: Stored on Passbolt -- GitHub token: A token with limited access has been created and added to the repository https://github.com/vector-im/element-android as secret DANGER_GITHUB_API_TOKEN. This token is not saved anywhere else. In case of problem, just delete it and create a new one, then update the secret. +- GitHub token: A token with limited access has been created and added to the repository https://github.com/element-hq/element-x-android as secret DANGER_GITHUB_API_TOKEN. This token is not saved anywhere else. In case of problem, just delete it and create a new one, then update the secret. PRs from forks do not always have access to the secret `secrets.DANGER_GITHUB_API_TOKEN`, so `secrets.GITHUB_TOKEN` is also provided to the job environment. If `secrets.DANGER_GITHUB_API_TOKEN` is available, it will be used, so user `ElementBot` will comment the PR. Else `secrets.GITHUB_TOKEN` will be used, and bot `github-actions` will comment the PR. diff --git a/docs/debug_proxying.md b/docs/debug_proxying.md index 77c109461b..a28163c959 100644 --- a/docs/debug_proxying.md +++ b/docs/debug_proxying.md @@ -8,15 +8,15 @@ 1) Disable "Use Android Studio HTTP proxy settings" and pick "Manual proxy configuration". 1) Set `127.0.0.1` as "Host name" and `8080` as "Port number". 1) Click "Apply" and verify that "Proxy status" is "Success" and close the settings window. - Screenshot 2023-10-04 at 14 48 47 + Screenshot 2023-10-04 at 14 48 47 1) Install the mitmproxy CA cert (this is needed to see traffic from java/kotlin code, it's not needed for traffic coming from native code e.g. the matrix-rust-sdk). 1) Open the emulator Chrome browser app 1) Go to the url `mitm.it` 1) Follow the instructions to install the CA cert on Android devices. - Screenshot 2023-10-04 at 14 51 27 + Screenshot 2023-10-04 at 14 51 27 1) Slightly modify the Element X app source code. 1) Go to the `RustMatrixClientFactory.create()` method. 1) Add `.disableSslVerification()` in the `ClientBuilder` method chain. 1) Build and run the Element X app. 1) Enjoy, you will see all the traffic in mitmproxy's web interface. - Screenshot 2023-10-04 at 14 50 03 + Screenshot 2023-10-04 at 14 50 03 diff --git a/docs/design.md b/docs/design.md index 58723eb28d..bdcaba199f 100644 --- a/docs/design.md +++ b/docs/design.md @@ -64,7 +64,7 @@ A comprehensive [color definition documentation](https://compound.element.io/?pa Most icons should be available as part of the [Compound icon library](https://compound.element.io/?path=/docs/tokens-icons--docs) All drawable are auto-generated as part of the design tokens library. You can find -all assets in [`vector-im/compound-design-tokens#assets/android`](https://github.com/vector-im/compound-design-tokens/tree/develop/assets/android) +all assets in [`element-hq/compound-design-tokens#assets/android`](https://github.com/element-hq/compound-design-tokens/tree/main/assets/android) If you are missing an icon, follow to [contribution guidelines for icons](https://www.figma.com/file/gkNXqPoiJhEv2wt0EJpew4/Compound-Icons?type=design&node-id=178-3119&t=j2uSJD9xPXJn5aRM-0) diff --git a/docs/install_from_github_release.md b/docs/install_from_github_release.md index de395f737a..069e1d97ec 100644 --- a/docs/install_from_github_release.md +++ b/docs/install_from_github_release.md @@ -18,11 +18,11 @@ The easiest way to do that is to use the debug signature that is shared between You can clone the project by running: ```bash -git clone git@github.com:vector-im/element-x-android.git +git clone git@github.com:element-hq/element-x-android.git ``` or ```bash -git clone https://github.com/vector-im/element-x-android.git +git clone https://github.com/element-hq/element-x-android.git ``` You will also need to install [bundletool](https://developer.android.com/studio/command-line/bundletool). On MacOS, you can run the following command: @@ -33,7 +33,7 @@ brew install bundletool ## Steps -1. Open the GitHub release that you want to install from https://github.com/vector-im/element-x-android/releases +1. Open the GitHub release that you want to install from https://github.com/element-hq/element-x-android/releases 2. Download the asset `app-release-signed.aab` 3. Navigate to the folder where you cloned the project and run the following command: ```bash diff --git a/docs/oidc.md b/docs/oidc.md index 0e4ad44852..94b22660c0 100644 --- a/docs/oidc.md +++ b/docs/oidc.md @@ -1,14 +1,14 @@ This file contains some rough notes about Oidc implementation, with some examples of actual data. -[ios implementation](https://github.com/vector-im/element-x-ios/compare/develop...doug/oidc-temp) +[ios implementation](https://github.com/element-hq/element-x-ios/compare/develop...doug/oidc-temp) Rust sdk branch: https://github.com/matrix-org/matrix-rust-sdk/tree/oidc-ffi Figma https://www.figma.com/file/o9p34zmiuEpZRyvZXJZAYL/FTUE?node-id=133-5426&t=yQXKeANatk6keoZF-0 -Server list: https://github.com/vector-im/oidc-playground +Server list: https://github.com/element-hq/oidc-playground -Metadata iOS: (from https://github.com/vector-im/element-x-ios/blob/5f9d07377cebc4f21d9668b1a25f6e3bb22f64a1/ElementX/Sources/Services/Authentication/AuthenticationServiceProxy.swift#L28) +Metadata iOS: (from https://github.com/element-hq/element-x-ios/blob/5f9d07377cebc4f21d9668b1a25f6e3bb22f64a1/ElementX/Sources/Services/Authentication/AuthenticationServiceProxy.swift#L28) clientName: InfoPlistReader.main.bundleDisplayName, redirectUri: "io.element:/callback", diff --git a/docs/pull_request.md b/docs/pull_request.md index 6144dd0d92..7ff71a30e7 100644 --- a/docs/pull_request.md +++ b/docs/pull_request.md @@ -86,13 +86,13 @@ Exceptions can occur: We use automatic assignment for PR reviews. **A PR is automatically routed by GitHub to one team member** using the round robin algorithm. Additional reviewers can be used for complex changes or when the first reviewer is not confident enough on the changes. The process is the following: -- The PR creator selects the [element-x-android-reviewers](https://github.com/orgs/vector-im/teams/element-x-android-reviewers) team as a reviewer. +- The PR creator selects the [element-x-android-reviewers](https://github.com/orgs/element-hq/teams/element-x-android-reviewers) team as a reviewer. - GitHub automatically assign the reviewer. If the reviewer is not available (holiday, etc.), remove them and set again the team, GitHub will select another reviewer. - Alternatively, the PR creator can directly assign specific people if they have another Android developer in their team or they think a specific reviewer should take a look at their PR. - Reviewers get a notification to make the review: they review the code following the good practice (see the rest of this document). - After making their own review, if they feel not confident enough, they can ask another person for a full review, or they can tag someone within a PR comment to check specific lines. -For PRs coming from the community, the issue wrangler can assign either the team [element-x-android-reviewers](https://github.com/orgs/vector-im/teams/element-x-android-reviewers) or any member directly. +For PRs coming from the community, the issue wrangler can assign either the team [element-x-android-reviewers](https://github.com/orgs/element-hq/teams/element-x-android-reviewers) or any member directly. ##### PR review time diff --git a/docs/screenshot_testing.md b/docs/screenshot_testing.md index cd9af50cbc..eb973af087 100644 --- a/docs/screenshot_testing.md +++ b/docs/screenshot_testing.md @@ -30,7 +30,7 @@ If installed correctly, `git push` and `git pull` will now include LFS content. ## Recording -Recording of screenshots is done by triggering the GitHub action [Record screenshots](https://github.com/vector-im/element-x-android/actions/workflows/recordScreenshots.yml), to avoid differences of generated binary files (png images) depending on developers' environment. +Recording of screenshots is done by triggering the GitHub action [Record screenshots](https://github.com/element-hq/element-x-android/actions/workflows/recordScreenshots.yml), to avoid differences of generated binary files (png images) depending on developers' environment. So basically, you will create a branch, do some commits with your work on it, then push your branch, trigger the GitHub action to record the screenshots (only if you think preview may have changed), and finally create a pull request. The GitHub action will record the screenshots and commit the changes to the branch. diff --git a/fastlane/metadata/android/en-US/changelogs/40001020.txt b/fastlane/metadata/android/en-US/changelogs/40001020.txt index 8aacdd89af..54b983499a 100644 --- a/fastlane/metadata/android/en-US/changelogs/40001020.txt +++ b/fastlane/metadata/android/en-US/changelogs/40001020.txt @@ -1,2 +1,2 @@ First release of Element X 🚀! -Full changelog: https://github.com/vector-im/element-x-android/releases +Full changelog: https://github.com/element-hq/element-x-android/releases diff --git a/fastlane/metadata/android/en-US/changelogs/40001040.txt b/fastlane/metadata/android/en-US/changelogs/40001040.txt index 2593704785..8b8ede35c3 100644 --- a/fastlane/metadata/android/en-US/changelogs/40001040.txt +++ b/fastlane/metadata/android/en-US/changelogs/40001040.txt @@ -1,2 +1,2 @@ Main changes in this version: bug fixes and add OIDC support. -Full changelog: https://github.com/vector-im/element-x-android/releases +Full changelog: https://github.com/element-hq/element-x-android/releases diff --git a/fastlane/metadata/android/en-US/changelogs/40001050.txt b/fastlane/metadata/android/en-US/changelogs/40001050.txt index 2593704785..8b8ede35c3 100644 --- a/fastlane/metadata/android/en-US/changelogs/40001050.txt +++ b/fastlane/metadata/android/en-US/changelogs/40001050.txt @@ -1,2 +1,2 @@ Main changes in this version: bug fixes and add OIDC support. -Full changelog: https://github.com/vector-im/element-x-android/releases +Full changelog: https://github.com/element-hq/element-x-android/releases diff --git a/fastlane/metadata/android/en-US/changelogs/40001060.txt b/fastlane/metadata/android/en-US/changelogs/40001060.txt index ff4f86ce7e..b2d056f013 100644 --- a/fastlane/metadata/android/en-US/changelogs/40001060.txt +++ b/fastlane/metadata/android/en-US/changelogs/40001060.txt @@ -1,2 +1,2 @@ Main changes in this version: bugfixes. -Full changelog: https://github.com/vector-im/element-x-android/releases +Full changelog: https://github.com/element-hq/element-x-android/releases diff --git a/fastlane/metadata/android/en-US/changelogs/40002000.txt b/fastlane/metadata/android/en-US/changelogs/40002000.txt index 97ceaa8d5a..19e1babb9d 100644 --- a/fastlane/metadata/android/en-US/changelogs/40002000.txt +++ b/fastlane/metadata/android/en-US/changelogs/40002000.txt @@ -1,2 +1,2 @@ Main changes in this version: Element Call, design update, bugfixes -Full changelog: https://github.com/vector-im/element-x-android/releases +Full changelog: https://github.com/element-hq/element-x-android/releases diff --git a/fastlane/metadata/android/en-US/changelogs/40002010.txt b/fastlane/metadata/android/en-US/changelogs/40002010.txt index 97ceaa8d5a..19e1babb9d 100644 --- a/fastlane/metadata/android/en-US/changelogs/40002010.txt +++ b/fastlane/metadata/android/en-US/changelogs/40002010.txt @@ -1,2 +1,2 @@ Main changes in this version: Element Call, design update, bugfixes -Full changelog: https://github.com/vector-im/element-x-android/releases +Full changelog: https://github.com/element-hq/element-x-android/releases diff --git a/fastlane/metadata/android/en-US/changelogs/40002020.txt b/fastlane/metadata/android/en-US/changelogs/40002020.txt index 882eb88054..f9c0486e89 100644 --- a/fastlane/metadata/android/en-US/changelogs/40002020.txt +++ b/fastlane/metadata/android/en-US/changelogs/40002020.txt @@ -1,2 +1,2 @@ Main changes in this version: bugfixes -Full changelog: https://github.com/vector-im/element-x-android/releases +Full changelog: https://github.com/element-hq/element-x-android/releases diff --git a/fastlane/metadata/android/en-US/changelogs/40002030.txt b/fastlane/metadata/android/en-US/changelogs/40002030.txt index ff4f86ce7e..b2d056f013 100644 --- a/fastlane/metadata/android/en-US/changelogs/40002030.txt +++ b/fastlane/metadata/android/en-US/changelogs/40002030.txt @@ -1,2 +1,2 @@ Main changes in this version: bugfixes. -Full changelog: https://github.com/vector-im/element-x-android/releases +Full changelog: https://github.com/element-hq/element-x-android/releases diff --git a/fastlane/metadata/android/en-US/changelogs/40002040.txt b/fastlane/metadata/android/en-US/changelogs/40002040.txt index ff4f86ce7e..b2d056f013 100644 --- a/fastlane/metadata/android/en-US/changelogs/40002040.txt +++ b/fastlane/metadata/android/en-US/changelogs/40002040.txt @@ -1,2 +1,2 @@ Main changes in this version: bugfixes. -Full changelog: https://github.com/vector-im/element-x-android/releases +Full changelog: https://github.com/element-hq/element-x-android/releases diff --git a/fastlane/metadata/android/en-US/changelogs/40003000.txt b/fastlane/metadata/android/en-US/changelogs/40003000.txt index 0634ffa760..fb55ecbe98 100644 --- a/fastlane/metadata/android/en-US/changelogs/40003000.txt +++ b/fastlane/metadata/android/en-US/changelogs/40003000.txt @@ -1,2 +1,2 @@ Main changes in this version: Element Call, voice message. -Full changelog: https://github.com/vector-im/element-x-android/releases +Full changelog: https://github.com/element-hq/element-x-android/releases diff --git a/fastlane/metadata/android/en-US/changelogs/40003010.txt b/fastlane/metadata/android/en-US/changelogs/40003010.txt index b2c89b804f..cccc7477c2 100644 --- a/fastlane/metadata/android/en-US/changelogs/40003010.txt +++ b/fastlane/metadata/android/en-US/changelogs/40003010.txt @@ -1,2 +1,2 @@ Main changes in this version: Mainly bug fixes. -Full changelog: https://github.com/vector-im/element-x-android/releases +Full changelog: https://github.com/element-hq/element-x-android/releases diff --git a/fastlane/metadata/android/en-US/changelogs/40003020.txt b/fastlane/metadata/android/en-US/changelogs/40003020.txt index 415e29b2bf..610f99ec10 100644 --- a/fastlane/metadata/android/en-US/changelogs/40003020.txt +++ b/fastlane/metadata/android/en-US/changelogs/40003020.txt @@ -4,4 +4,4 @@ Main changes in this version: - Adding mentions to a message is now possible, but it's disabled by default as it's a work in progress. They can be enabled in the developer options. - Voice messages behavior changed: there is no need to keep pressing to record, to start recording a message just tap on the mic button, then tap again to stop recording. - Added a marker in the timeline to indicate the starting point of the room messages. -Full changelog: https://github.com/vector-im/element-x-android/releases +Full changelog: https://github.com/element-hq/element-x-android/releases diff --git a/fastlane/metadata/android/en-US/changelogs/40004000.txt b/fastlane/metadata/android/en-US/changelogs/40004000.txt new file mode 100644 index 0000000000..811c504542 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/40004000.txt @@ -0,0 +1,13 @@ +Main changes in this version: + +- New timeline messages rendering. +- Added support for user mentions. +- Enabled chat backup so you can restore previous message history. +- Added read receipts. +- Several improvements to polls, including poll history. +- Several UI/UX improvements. +- Set a default power level to join room calls. +- Added an option to disable notifications for invites. +- Fixed a bug with the text composer and suggestions on Android 14. + +Full changelog: https://github.com/element-hq/element-x-android/releases diff --git a/features/analytics/api/src/main/res/values-hu/translations.xml b/features/analytics/api/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..13f7026038 --- /dev/null +++ b/features/analytics/api/src/main/res/values-hu/translations.xml @@ -0,0 +1,7 @@ + + + "Elemzési adatok megosztása" + "Anonim használati adatok megosztása a problémák azonosítása érdekében." + "%1$s olvashatja el a feltételeinket." + "Itt" + diff --git a/features/analytics/api/src/main/res/values-in/translations.xml b/features/analytics/api/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..ffe3dc729e --- /dev/null +++ b/features/analytics/api/src/main/res/values-in/translations.xml @@ -0,0 +1,7 @@ + + + "Bagikan data analitik" + "Bagikan data penggunaan anonim untuk membantu kami mengidentifikasi masalah." + "Anda dapat membaca semua persyaratan kami %1$s." + "di sini" + diff --git a/features/analytics/impl/build.gradle.kts b/features/analytics/impl/build.gradle.kts index 3deb202ebc..02b7e50b15 100644 --- a/features/analytics/impl/build.gradle.kts +++ b/features/analytics/impl/build.gradle.kts @@ -39,6 +39,7 @@ dependencies { implementation(projects.libraries.uiStrings) api(projects.features.analytics.api) api(projects.services.analytics.api) + implementation(projects.appconfig) implementation(libs.androidx.datastore.preferences) implementation(libs.androidx.browser) ksp(libs.showkase.processor) diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInNode.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInNode.kt index ab060a51cf..480ccfa512 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInNode.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInNode.kt @@ -17,7 +17,6 @@ package io.element.android.features.analytics.impl import android.app.Activity -import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -27,7 +26,8 @@ import com.bumble.appyx.core.plugin.Plugin import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode -import io.element.android.features.analytics.api.Config +import io.element.android.appconfig.AnalyticsConfig +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab import io.element.android.libraries.di.AppScope @@ -39,13 +39,13 @@ class AnalyticsOptInNode @AssistedInject constructor( ) : Node(buildContext, plugins = plugins) { private fun onClickTerms(activity: Activity, darkTheme: Boolean) { - activity.openUrlInChromeCustomTab(null, darkTheme, Config.POLICY_LINK) + activity.openUrlInChromeCustomTab(null, darkTheme, AnalyticsConfig.POLICY_LINK) } @Composable override fun View(modifier: Modifier) { val activity = LocalContext.current as Activity - val isDark = MaterialTheme.colors.isLight.not() + val isDark = ElementTheme.isLightTheme.not() val state = presenter.present() AnalyticsOptInView( state = state, diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt index 7aa007c9e5..9f75d4bd6e 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt @@ -40,24 +40,24 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.appconfig.AnalyticsConfig +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.analytics.api.AnalyticsOptInEvents -import io.element.android.features.analytics.api.Config import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.atomic.organisms.InfoListItem import io.element.android.libraries.designsystem.atomic.organisms.InfoListOrganism import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.text.buildAnnotatedStringWithStyledPart import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.ButtonSize import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.designsystem.theme.temporaryColorBgSpecial -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.designsystem.utils.LogCompositions -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.persistentListOf @@ -119,7 +119,7 @@ private fun AnalyticsOptInHeader( color = Color.Unspecified, underline = false, bold = true, - tagAndLink = LINK_TAG to Config.POLICY_LINK, + tagAndLink = LINK_TAG to AnalyticsConfig.POLICY_LINK, ) ClickableText( text = text, @@ -147,7 +147,7 @@ private fun CheckIcon(modifier: Modifier = Modifier) { .size(20.dp) .background(color = MaterialTheme.colorScheme.background, shape = CircleShape) .padding(2.dp), - resourceId = CommonDrawables.ic_compound_check, + imageVector = CompoundIcons.Check, contentDescription = null, tint = ElementTheme.colors.textActionAccent, ) diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/preferences/DefaultAnalyticsPreferencesPresenter.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/preferences/DefaultAnalyticsPreferencesPresenter.kt index 06431402a6..8b46ff3fa3 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/preferences/DefaultAnalyticsPreferencesPresenter.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/preferences/DefaultAnalyticsPreferencesPresenter.kt @@ -20,8 +20,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.rememberCoroutineScope import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.appconfig.AnalyticsConfig import io.element.android.features.analytics.api.AnalyticsOptInEvents -import io.element.android.features.analytics.api.Config import io.element.android.features.analytics.api.preferences.AnalyticsPreferencesPresenter import io.element.android.features.analytics.api.preferences.AnalyticsPreferencesState import io.element.android.libraries.core.meta.BuildMeta @@ -52,7 +52,7 @@ class DefaultAnalyticsPreferencesPresenter @Inject constructor( return AnalyticsPreferencesState( applicationName = buildMeta.applicationName, isEnabled = isEnabled.value, - policyUrl = Config.POLICY_LINK, + policyUrl = AnalyticsConfig.POLICY_LINK, eventSink = ::handleEvents ) } diff --git a/features/analytics/impl/src/main/res/values-hu/translations.xml b/features/analytics/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..dea908fe08 --- /dev/null +++ b/features/analytics/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,10 @@ + + + "Nem rögzítünk vagy profilozunk személyes adatokat" + "Anonim használati adatok megosztása a problémák azonosítása érdekében." + "%1$s olvashatja el a feltételeinket." + "Itt" + "Ezt bármikor kikapcsolhatja" + "Adatait nem osztjuk meg harmadik felekkel" + "Segítsen az %1$s fejlesztésében" + diff --git a/features/analytics/impl/src/main/res/values-in/translations.xml b/features/analytics/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..655b0c6d88 --- /dev/null +++ b/features/analytics/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,10 @@ + + + "Kami tidak akan merekam atau memprofil data pribadi apa pun" + "Bagikan data penggunaan anonim untuk membantu kami mengidentifikasi masalah." + "Anda dapat membaca semua persyaratan kami %1$s." + "di sini" + "Anda dapat mematikan ini kapan saja" + "Kami tidak akan membagikan data Anda dengan pihak ketiga" + "Bantu sempurnakan %1$s" + diff --git a/features/cachecleaner/impl/src/test/kotlin/io/element/android/features/cachecleaner/impl/DefaultCacheCleanerTest.kt b/features/cachecleaner/impl/src/test/kotlin/io/element/android/features/cachecleaner/impl/DefaultCacheCleanerTest.kt index 7fe70028ac..f35bc518fa 100644 --- a/features/cachecleaner/impl/src/test/kotlin/io/element/android/features/cachecleaner/impl/DefaultCacheCleanerTest.kt +++ b/features/cachecleaner/impl/src/test/kotlin/io/element/android/features/cachecleaner/impl/DefaultCacheCleanerTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.cachecleaner.impl -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest @@ -46,9 +46,9 @@ class DefaultCacheCleanerTest { // Check the files are gone but the sub dirs are not. DefaultCacheCleaner.SUBDIRS_TO_CLEANUP.forEach { File(temporaryFolder.root, it).apply { - Truth.assertThat(exists()).isTrue() - Truth.assertThat(isDirectory).isTrue() - Truth.assertThat(listFiles()).isEmpty() + assertThat(exists()).isTrue() + assertThat(isDirectory).isTrue() + assertThat(listFiles()).isEmpty() } } } diff --git a/features/call/src/main/kotlin/io/element/android/features/call/ui/CallScreenView.kt b/features/call/src/main/kotlin/io/element/android/features/call/ui/CallScreenView.kt index acc01e6149..f2c8a0cb93 100644 --- a/features/call/src/main/kotlin/io/element/android/features/call/ui/CallScreenView.kt +++ b/features/call/src/main/kotlin/io/element/android/features/call/ui/CallScreenView.kt @@ -33,6 +33,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.stringResource import androidx.compose.ui.viewinterop.AndroidView +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.call.R import io.element.android.features.call.utils.WebViewWidgetMessageInterceptor import io.element.android.libraries.architecture.Async @@ -42,7 +43,6 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.designsystem.utils.CommonDrawables typealias RequestPermissionCallback = (Array) -> Unit @@ -64,7 +64,7 @@ internal fun CallScreenView( title = { Text(stringResource(R.string.element_call)) }, navigationIcon = { BackButton( - resourceId = CommonDrawables.ic_compound_close, + imageVector = CompoundIcons.Close, onClick = { state.eventSink(CallScreenEvents.Hangup) } ) } diff --git a/features/call/src/main/kotlin/io/element/android/features/call/ui/ElementCallActivity.kt b/features/call/src/main/kotlin/io/element/android/features/call/ui/ElementCallActivity.kt index 54a41ea31d..2729d71a7e 100644 --- a/features/call/src/main/kotlin/io/element/android/features/call/ui/ElementCallActivity.kt +++ b/features/call/src/main/kotlin/io/element/android/features/call/ui/ElementCallActivity.kt @@ -37,16 +37,16 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.core.content.IntentCompat import com.bumble.appyx.core.integrationpoint.NodeComponentActivity +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.theme.Theme +import io.element.android.compound.theme.isDark +import io.element.android.compound.theme.mapToTheme import io.element.android.features.call.CallForegroundService import io.element.android.features.call.CallType import io.element.android.features.call.di.CallBindings import io.element.android.features.call.utils.CallIntentDataParser import io.element.android.features.preferences.api.store.PreferencesStore import io.element.android.libraries.architecture.bindings -import io.element.android.libraries.theme.ElementTheme -import io.element.android.libraries.theme.theme.Theme -import io.element.android.libraries.theme.theme.isDark -import io.element.android.libraries.theme.theme.mapToTheme import javax.inject.Inject class ElementCallActivity : NodeComponentActivity(), CallScreenNavigator { diff --git a/features/call/src/main/res/values-hu/translations.xml b/features/call/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..fee5a163bb --- /dev/null +++ b/features/call/src/main/res/values-hu/translations.xml @@ -0,0 +1,6 @@ + + + "Folyamatban lévő hívás" + "Koppintson a híváshoz való visszatéréshez" + "☎️ Hívás folyamatban" + diff --git a/features/call/src/main/res/values-in/translations.xml b/features/call/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..886dac5e33 --- /dev/null +++ b/features/call/src/main/res/values-in/translations.xml @@ -0,0 +1,6 @@ + + + "Panggilan berlangsung" + "Ketuk untuk kembali ke panggilan" + "☎️ Panggilan sedang berlangsung" + diff --git a/features/call/src/test/kotlin/io/element/android/features/call/MapWebkitPermissionsTest.kt b/features/call/src/test/kotlin/io/element/android/features/call/MapWebkitPermissionsTest.kt index 55b5f16771..fa60b348b1 100644 --- a/features/call/src/test/kotlin/io/element/android/features/call/MapWebkitPermissionsTest.kt +++ b/features/call/src/test/kotlin/io/element/android/features/call/MapWebkitPermissionsTest.kt @@ -39,7 +39,6 @@ class MapWebkitPermissionsTest { @Test fun `given any other permission, it returns nothing`() { val permission = mapWebkitPermissions(arrayOf(PermissionRequest.RESOURCE_PROTECTED_MEDIA_ID)) - assertThat(permission).isEqualTo(emptyList()) + assertThat(permission).isEmpty() } - } diff --git a/features/createroom/api/src/main/kotlin/io/element/android/features/createroom/api/StartDMAction.kt b/features/createroom/api/src/main/kotlin/io/element/android/features/createroom/api/StartDMAction.kt new file mode 100644 index 0000000000..ef95f15dce --- /dev/null +++ b/features/createroom/api/src/main/kotlin/io/element/android/features/createroom/api/StartDMAction.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.createroom.api + +import androidx.compose.runtime.MutableState +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.UserId + +interface StartDMAction { + /** + * Try to find an existing DM with the given user, or create one if none exists. + * @param userId The user to start a DM with. + * @param actionState The state to update with the result of the action. + */ + suspend fun execute(userId: UserId, actionState: MutableState>) +} diff --git a/features/createroom/impl/build.gradle.kts b/features/createroom/impl/build.gradle.kts index ae9818b727..6cd9b30741 100644 --- a/features/createroom/impl/build.gradle.kts +++ b/features/createroom/impl/build.gradle.kts @@ -67,6 +67,7 @@ dependencies { testImplementation(projects.libraries.mediaupload.test) testImplementation(projects.libraries.permissions.test) testImplementation(projects.libraries.usersearch.test) + testImplementation(projects.features.createroom.test) testImplementation(projects.tests.testutils) ksp(libs.showkase.processor) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/ConfigureRoomFlowNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/ConfigureRoomFlowNode.kt index 3b96ac3edd..78fbc6f77a 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/ConfigureRoomFlowNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/ConfigureRoomFlowNode.kt @@ -19,7 +19,6 @@ package io.element.android.features.createroom.impl import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin @@ -32,8 +31,8 @@ import io.element.android.anvilannotations.ContributesNode import io.element.android.features.createroom.impl.addpeople.AddPeopleNode import io.element.android.features.createroom.impl.configureroom.ConfigureRoomNode import io.element.android.features.createroom.impl.di.CreateRoomComponent -import io.element.android.libraries.architecture.BackstackNode -import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler +import io.element.android.libraries.architecture.BackstackView +import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.DaggerComponentOwner @@ -45,7 +44,7 @@ class ConfigureRoomFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, ) : DaggerComponentOwner, - BackstackNode( + BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Root, savedStateMap = buildContext.savedStateMap, @@ -77,21 +76,17 @@ class ConfigureRoomFlowNode @AssistedInject constructor( backstack.push(NavTarget.ConfigureRoom) } } - createNode(context = buildContext, plugins = listOf(callback)) + createNode(buildContext = buildContext, plugins = listOf(callback)) } NavTarget.ConfigureRoom -> { val callbacks = plugins() - createNode(context = buildContext, plugins = callbacks) + createNode(buildContext = buildContext, plugins = callbacks) } } } @Composable override fun View(modifier: Modifier) { - Children( - navModel = backstack, - modifier = modifier, - transitionHandler = rememberDefaultTransitionHandler() - ) + BackstackView() } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt index 207ab73e66..643987e0a3 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt @@ -19,7 +19,6 @@ package io.element.android.features.createroom.impl import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin @@ -32,8 +31,8 @@ import io.element.android.anvilannotations.ContributesNode import io.element.android.features.createroom.api.CreateRoomEntryPoint import io.element.android.features.createroom.impl.configureroom.ConfigureRoomNode import io.element.android.features.createroom.impl.root.CreateRoomRootNode -import io.element.android.libraries.architecture.BackstackNode -import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler +import io.element.android.libraries.architecture.BackstackView +import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId @@ -43,7 +42,7 @@ import kotlinx.parcelize.Parcelize class CreateRoomFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, -) : BackstackNode( +) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Root, savedStateMap = buildContext.savedStateMap, @@ -72,7 +71,7 @@ class CreateRoomFlowNode @AssistedInject constructor( plugins().forEach { it.onSuccess(roomId) } } } - createNode(context = buildContext, plugins = listOf(callback)) + createNode(buildContext = buildContext, plugins = listOf(callback)) } NavTarget.NewRoom -> { val callback = object : ConfigureRoomNode.Callback { @@ -80,17 +79,13 @@ class CreateRoomFlowNode @AssistedInject constructor( plugins().forEach { it.onSuccess(roomId) } } } - createNode(context = buildContext, plugins = listOf(callback)) + createNode(buildContext = buildContext, plugins = listOf(callback)) } } } @Composable override fun View(modifier: Modifier) { - Children( - navModel = backstack, - modifier = modifier, - transitionHandler = rememberDefaultTransitionHandler() - ) + BackstackView() } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/DefaultStartDMAction.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/DefaultStartDMAction.kt new file mode 100644 index 0000000000..7145ac671e --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/DefaultStartDMAction.kt @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.createroom.impl + +import androidx.compose.runtime.MutableState +import com.squareup.anvil.annotations.ContributesBinding +import im.vector.app.features.analytics.plan.CreatedRoom +import io.element.android.features.createroom.api.StartDMAction +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.room.StartDMResult +import io.element.android.libraries.matrix.api.room.startDM +import io.element.android.services.analytics.api.AnalyticsService +import javax.inject.Inject + +@ContributesBinding(SessionScope::class) +class DefaultStartDMAction @Inject constructor( + private val matrixClient: MatrixClient, + private val analyticsService: AnalyticsService, +) : StartDMAction { + + override suspend fun execute(userId: UserId, actionState: MutableState>) { + actionState.value = Async.Loading() + when (val result = matrixClient.startDM(userId)) { + is StartDMResult.Success -> { + if (result.isNew) { + analyticsService.capture(CreatedRoom(isDM = true)) + } + actionState.value = Async.Success(result.roomId) + } + is StartDMResult.Failure -> { + actionState.value = Async.Failure(result.throwable) + } + } + } +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt index 40a597b649..1a8540cd0e 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt @@ -32,23 +32,23 @@ import io.element.android.features.createroom.impl.components.UserListView import io.element.android.features.createroom.impl.userlist.UserListEvents import io.element.android.features.createroom.impl.userlist.UserListState import io.element.android.libraries.designsystem.components.button.BackButton -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @OptIn(ExperimentalLayoutApi::class) @Composable fun AddPeopleView( state: UserListState, + onBackPressed: () -> Unit, + onNextPressed: () -> Unit, modifier: Modifier = Modifier, - onBackPressed: () -> Unit = {}, - onNextPressed: () -> Unit = {}, ) { Scaffold( modifier = modifier, @@ -77,6 +77,8 @@ fun AddPeopleView( .fillMaxWidth(), state = state, showBackButton = false, + onUserSelected = { }, + onUserDeselected = {}, ) } } @@ -86,9 +88,9 @@ fun AddPeopleView( @Composable private fun AddPeopleViewTopBar( hasSelectedUsers: Boolean, + onBackPressed: () -> Unit, + onNextPressed: () -> Unit, modifier: Modifier = Modifier, - onBackPressed: () -> Unit = {}, - onNextPressed: () -> Unit = {}, ) { TopAppBar( modifier = modifier, @@ -112,5 +114,9 @@ private fun AddPeopleViewTopBar( @PreviewsDayNight @Composable internal fun AddPeopleViewPreview(@PreviewParameter(AddPeopleUserListStateProvider::class) state: UserListState) = ElementPreview { - AddPeopleView(state = state) + AddPeopleView( + state = state, + onBackPressed = {}, + onNextPressed = {}, + ) } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt index 00b4206a73..a7f7c80912 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt @@ -36,14 +36,14 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.RadioButton import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable fun RoomPrivacyOption( roomPrivacyItem: RoomPrivacyItem, + onOptionSelected: (RoomPrivacyItem) -> Unit, modifier: Modifier = Modifier, isSelected: Boolean = false, - onOptionSelected: (RoomPrivacyItem) -> Unit = {}, ) { Row( modifier @@ -58,7 +58,7 @@ fun RoomPrivacyOption( Icon( modifier = Modifier.padding(horizontal = 8.dp), resourceId = roomPrivacyItem.icon, - contentDescription = "", + contentDescription = null, tint = MaterialTheme.colorScheme.secondary, ) @@ -97,10 +97,12 @@ internal fun RoomPrivacyOptionPreview() = ElementPreview { Column { RoomPrivacyOption( roomPrivacyItem = aRoomPrivacyItem, + onOptionSelected = {}, isSelected = true, ) RoomPrivacyOption( roomPrivacyItem = aRoomPrivacyItem, + onOptionSelected = {}, isSelected = false, ) } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchMultipleUsersResultItem.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchMultipleUsersResultItem.kt index be6a7e0a79..20f2c78d8a 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchMultipleUsersResultItem.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchMultipleUsersResultItem.kt @@ -33,8 +33,8 @@ import io.element.android.libraries.usersearch.api.UserSearchResult fun SearchMultipleUsersResultItem( searchResult: UserSearchResult, isUserSelected: Boolean, + onCheckedChange: (Boolean) -> Unit, modifier: Modifier = Modifier, - onCheckedChange: (Boolean) -> Unit = {}, ) { if (searchResult.isUnresolved) { CheckableUnresolvedUserRow( @@ -62,12 +62,40 @@ internal fun SearchMultipleUsersResultItemPreview() = ElementThemedPreview { Con @Composable private fun ContentToPreview() { Column { - SearchMultipleUsersResultItem(searchResult = UserSearchResult(aMatrixUser(), isUnresolved = false), isUserSelected = false) + SearchMultipleUsersResultItem( + searchResult = UserSearchResult( + aMatrixUser(), + isUnresolved = false + ), + isUserSelected = false, + onCheckedChange = {} + ) HorizontalDivider() - SearchMultipleUsersResultItem(searchResult = UserSearchResult(aMatrixUser(), isUnresolved = false), isUserSelected = true) + SearchMultipleUsersResultItem( + searchResult = UserSearchResult( + aMatrixUser(), + isUnresolved = false + ), + isUserSelected = true, + onCheckedChange = {} + ) HorizontalDivider() - SearchMultipleUsersResultItem(searchResult = UserSearchResult(aMatrixUser(), isUnresolved = true), isUserSelected = false) + SearchMultipleUsersResultItem( + searchResult = UserSearchResult( + aMatrixUser(), + isUnresolved = true + ), + isUserSelected = false, + onCheckedChange = {} + ) HorizontalDivider() - SearchMultipleUsersResultItem(searchResult = UserSearchResult(aMatrixUser(), isUnresolved = true), isUserSelected = true) + SearchMultipleUsersResultItem( + searchResult = UserSearchResult( + aMatrixUser(), + isUnresolved = true + ), + isUserSelected = true, + onCheckedChange = {} + ) } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchSingleUserResultItem.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchSingleUserResultItem.kt index 69b528f61c..dc4f936acb 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchSingleUserResultItem.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchSingleUserResultItem.kt @@ -33,8 +33,8 @@ import io.element.android.libraries.usersearch.api.UserSearchResult @Composable fun SearchSingleUserResultItem( searchResult: UserSearchResult, + onClick: () -> Unit, modifier: Modifier = Modifier, - onClick: () -> Unit = {}, ) { if (searchResult.isUnresolved) { UnresolvedUserRow( @@ -58,8 +58,14 @@ internal fun SearchSingleUserResultItemPreview() = ElementThemedPreview { Conten @Composable private fun ContentToPreview() { Column { - SearchSingleUserResultItem(searchResult = UserSearchResult(aMatrixUser(), isUnresolved = false)) + SearchSingleUserResultItem( + searchResult = UserSearchResult(aMatrixUser(), isUnresolved = false), + onClick = {}, + ) HorizontalDivider() - SearchSingleUserResultItem(searchResult = UserSearchResult(aMatrixUser(), isUnresolved = true)) + SearchSingleUserResultItem( + searchResult = UserSearchResult(aMatrixUser(), isUnresolved = true), + onClick = {}, + ) } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchUserBar.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchUserBar.kt index be2ba4ddb6..bc562627cc 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchUserBar.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchUserBar.kt @@ -52,13 +52,13 @@ fun SearchUserBar( selectedUsers: ImmutableList, active: Boolean, isMultiSelectionEnabled: Boolean, + onActiveChanged: (Boolean) -> Unit, + onTextChanged: (String) -> Unit, + onUserSelected: (MatrixUser) -> Unit, + onUserDeselected: (MatrixUser) -> Unit, modifier: Modifier = Modifier, showBackButton: Boolean = true, placeHolderTitle: String = stringResource(CommonStrings.common_search_for_someone), - onActiveChanged: (Boolean) -> Unit = {}, - onTextChanged: (String) -> Unit = {}, - onUserSelected: (MatrixUser) -> Unit = {}, - onUserDeselected: (MatrixUser) -> Unit = {}, ) { val columnState = rememberLazyListState() diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/UserListView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/UserListView.kt index 61a0fcbd1b..45d898577f 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/UserListView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/UserListView.kt @@ -26,18 +26,18 @@ import androidx.compose.ui.unit.dp import io.element.android.features.createroom.impl.userlist.UserListEvents import io.element.android.features.createroom.impl.userlist.UserListState import io.element.android.features.createroom.impl.userlist.UserListStateProvider -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.components.SelectedUsersList @Composable fun UserListView( state: UserListState, + onUserSelected: (MatrixUser) -> Unit, + onUserDeselected: (MatrixUser) -> Unit, modifier: Modifier = Modifier, showBackButton: Boolean = true, - onUserSelected: (MatrixUser) -> Unit = {}, - onUserDeselected: (MatrixUser) -> Unit = {}, ) { Column( modifier = modifier, @@ -79,5 +79,9 @@ fun UserListView( @PreviewsDayNight @Composable internal fun UserListViewPreview(@PreviewParameter(UserListStateProvider::class) state: UserListState) = ElementPreview { - UserListView(state = state) + UserListView( + state = state, + onUserSelected = {}, + onUserDeselected = {}, + ) } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index d12a6076b5..bf407dd4ec 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -63,7 +63,7 @@ import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet import io.element.android.libraries.matrix.ui.components.SelectedUsersList import io.element.android.libraries.matrix.ui.components.UnsavedAvatar import io.element.android.libraries.permissions.api.PermissionsView -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.launch @@ -167,9 +167,9 @@ fun ConfigureRoomView( @Composable private fun ConfigureRoomToolbar( isNextActionEnabled: Boolean, + onBackPressed: () -> Unit, + onNextPressed: () -> Unit, modifier: Modifier = Modifier, - onBackPressed: () -> Unit = {}, - onNextPressed: () -> Unit = {}, ) { TopAppBar( modifier = modifier, @@ -194,9 +194,9 @@ private fun ConfigureRoomToolbar( private fun RoomNameWithAvatar( avatarUri: Uri?, roomName: String, + onAvatarClick: () -> Unit, + onRoomNameChanged: (String) -> Unit, modifier: Modifier = Modifier, - onAvatarClick: () -> Unit = {}, - onRoomNameChanged: (String) -> Unit = {}, ) { Row( modifier = modifier, @@ -221,8 +221,8 @@ private fun RoomNameWithAvatar( @Composable private fun RoomTopic( topic: String, + onTopicChanged: (String) -> Unit, modifier: Modifier = Modifier, - onTopicChanged: (String) -> Unit = {}, ) { LabelledTextField( modifier = modifier, @@ -240,8 +240,8 @@ private fun RoomTopic( @Composable private fun RoomPrivacyOptions( selected: RoomPrivacy?, + onOptionSelected: (RoomPrivacyItem) -> Unit, modifier: Modifier = Modifier, - onOptionSelected: (RoomPrivacyItem) -> Unit = {}, ) { val items = roomPrivacyItems() Column(modifier = modifier.selectableGroup()) { diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt index 8193dfe5d9..ea5ce36192 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt @@ -19,8 +19,8 @@ package io.element.android.features.createroom.impl.configureroom import androidx.annotation.DrawableRes import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource +import io.element.android.libraries.designsystem.icons.CompoundDrawables import io.element.android.features.createroom.impl.R -import io.element.android.libraries.designsystem.utils.CommonDrawables import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList @@ -38,13 +38,13 @@ fun roomPrivacyItems(): ImmutableList { when (it) { RoomPrivacy.Private -> RoomPrivacyItem( privacy = it, - icon = CommonDrawables.ic_compound_lock, + icon = CompoundDrawables.ic_lock, title = stringResource(R.string.screen_create_room_private_option_title), description = stringResource(R.string.screen_create_room_private_option_description), ) RoomPrivacy.Public -> RoomPrivacyItem( privacy = it, - icon = CommonDrawables.ic_compound_public, + icon = CompoundDrawables.ic_public, title = stringResource(R.string.screen_create_room_public_option_title), description = stringResource(R.string.screen_create_room_public_option_description), ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt index 839e4cd69e..9b60924ca4 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt @@ -21,21 +21,16 @@ import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import im.vector.app.features.analytics.plan.CreatedRoom +import io.element.android.features.createroom.api.StartDMAction import io.element.android.features.createroom.impl.userlist.SelectionMode import io.element.android.features.createroom.impl.userlist.UserListDataStore import io.element.android.features.createroom.impl.userlist.UserListPresenter import io.element.android.features.createroom.impl.userlist.UserListPresenterArgs import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.core.meta.BuildMeta -import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.usersearch.api.UserRepository -import io.element.android.services.analytics.api.AnalyticsService -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import javax.inject.Inject @@ -43,8 +38,7 @@ class CreateRoomRootPresenter @Inject constructor( presenterFactory: UserListPresenter.Factory, userRepository: UserRepository, userListDataStore: UserListDataStore, - private val matrixClient: MatrixClient, - private val analyticsService: AnalyticsService, + private val startDMAction: StartDMAction, private val buildMeta: BuildMeta, ) : Presenter { @@ -61,37 +55,22 @@ class CreateRoomRootPresenter @Inject constructor( val userListState = presenter.present() val localCoroutineScope = rememberCoroutineScope() - val startDmAction: MutableState> = remember { mutableStateOf(Async.Uninitialized) } + val startDmActionState: MutableState> = remember { mutableStateOf(Async.Uninitialized) } fun handleEvents(event: CreateRoomRootEvents) { when (event) { - is CreateRoomRootEvents.StartDM -> localCoroutineScope.startDm(event.matrixUser, startDmAction) - CreateRoomRootEvents.CancelStartDM -> startDmAction.value = Async.Uninitialized + is CreateRoomRootEvents.StartDM -> localCoroutineScope.launch { + startDMAction.execute(event.matrixUser.userId, startDmActionState) + } + CreateRoomRootEvents.CancelStartDM -> startDmActionState.value = Async.Uninitialized } } return CreateRoomRootState( applicationName = buildMeta.applicationName, userListState = userListState, - startDmAction = startDmAction.value, + startDmAction = startDmActionState.value, eventSink = ::handleEvents, ) } - - private fun CoroutineScope.startDm(matrixUser: MatrixUser, startDmAction: MutableState>) = launch { - suspend { - matrixClient.findDM(matrixUser.userId).use { existingDM -> - existingDM?.roomId ?: createDM(matrixUser) - } - }.runCatchingUpdatingState(startDmAction) - } - - private suspend fun createDM(user: MatrixUser): RoomId { - return matrixClient - .createDM(user.userId) - .onSuccess { - analyticsService.capture(CreatedRoom(isDM = true)) - } - .getOrThrow() - } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt index 6bc6e33339..d1b910dae2 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt @@ -35,6 +35,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.libraries.designsystem.icons.CompoundDrawables import io.element.android.features.createroom.impl.R import io.element.android.features.createroom.impl.components.UserListView import io.element.android.libraries.designsystem.components.async.AsyncView @@ -46,20 +49,18 @@ import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @OptIn(ExperimentalLayoutApi::class) @Composable fun CreateRoomRootView( state: CreateRoomRootState, + onClosePressed: () -> Unit, + onNewRoomClicked: () -> Unit, + onOpenDM: (RoomId) -> Unit, + onInviteFriendsClicked: () -> Unit, modifier: Modifier = Modifier, - onClosePressed: () -> Unit = {}, - onNewRoomClicked: () -> Unit = {}, - onOpenDM: (RoomId) -> Unit = {}, - onInviteFriendsClicked: () -> Unit = {}, ) { Scaffold( modifier = modifier.fillMaxWidth(), @@ -81,6 +82,7 @@ fun CreateRoomRootView( onUserSelected = { state.eventSink(CreateRoomRootEvents.StartDM(it)) }, + onUserDeselected = { }, ) if (!state.userListState.isSearchActive) { @@ -111,8 +113,8 @@ fun CreateRoomRootView( @OptIn(ExperimentalMaterial3Api::class) @Composable private fun CreateRoomRootViewTopBar( + onClosePressed: () -> Unit, modifier: Modifier = Modifier, - onClosePressed: () -> Unit = {}, ) { TopAppBar( modifier = modifier, @@ -124,7 +126,7 @@ private fun CreateRoomRootViewTopBar( }, navigationIcon = { BackButton( - resourceId = CommonDrawables.ic_compound_close, + imageVector = CompoundIcons.Close, onClick = onClosePressed, ) } @@ -134,18 +136,18 @@ private fun CreateRoomRootViewTopBar( @Composable private fun CreateRoomActionButtonsList( state: CreateRoomRootState, + onNewRoomClicked: () -> Unit, + onInvitePeopleClicked: () -> Unit, modifier: Modifier = Modifier, - onNewRoomClicked: () -> Unit = {}, - onInvitePeopleClicked: () -> Unit = {}, ) { Column(modifier = modifier) { CreateRoomActionButton( - iconRes = CommonDrawables.ic_compound_plus, + iconRes = CompoundDrawables.ic_plus, text = stringResource(id = R.string.screen_create_room_action_create_room), onClick = onNewRoomClicked, ) CreateRoomActionButton( - iconRes = CommonDrawables.ic_compound_share_android, + iconRes = CompoundDrawables.ic_share_android, text = stringResource(id = CommonStrings.action_invite_friends_to_app, state.applicationName), onClick = onInvitePeopleClicked, ) @@ -156,8 +158,8 @@ private fun CreateRoomActionButtonsList( private fun CreateRoomActionButton( @DrawableRes iconRes: Int, text: String, + onClick: () -> Unit, modifier: Modifier = Modifier, - onClick: () -> Unit = {}, ) { Row( modifier = modifier @@ -187,5 +189,9 @@ internal fun CreateRoomRootViewPreview(@PreviewParameter(CreateRoomRootStateProv ElementPreview { CreateRoomRootView( state = state, + onClosePressed = {}, + onNewRoomClicked = {}, + onOpenDM = {}, + onInviteFriendsClicked = {}, ) } diff --git a/features/createroom/impl/src/main/res/values-hu/translations.xml b/features/createroom/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..29a403a091 --- /dev/null +++ b/features/createroom/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,15 @@ + + + "Új szoba" + "Hívja meg ismerőseit az Elementbe" + "Emberek meghívása" + "Hiba történt a szoba létrehozásakor" + "A szobában lévő üzenetek titkosítottak. A titkosítást utólag nem lehet kikapcsolni." + "Privát szoba (csak meghívással)" + "Az üzenetek nincsenek titkosítva, és bárki elolvashatja őket. A titkosítást később is engedélyezheti." + "Nyilvános szoba (bárki)" + "Szoba neve" + "Téma (nem kötelező)" + "Hiba történt a csevegés indításakor" + "Szoba létrehozása" + diff --git a/features/createroom/impl/src/main/res/values-in/translations.xml b/features/createroom/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..aff7eab8b7 --- /dev/null +++ b/features/createroom/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,15 @@ + + + "Ruangan baru" + "Undang teman ke Element" + "Undang seseorang" + "Terjadi kesalahan saat membuat ruangan" + "Pesan di ruangan ini dienkripsi. Enkripsi tidak dapat dinonaktifkan setelahnya." + "Ruangan pribadi (hanya undangan)" + "Pesan tidak dienkripsi dan siapa pun dapat membacanya. Anda dapat mengaktifkan enkripsi di kemudian hari." + "Ruang publik (siapa saja)" + "Nama ruangan" + "Topik (opsional)" + "Terjadi kesalahan saat mencoba memulai obrolan" + "Buat ruangan" + diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/DefaultStartDMActionTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/DefaultStartDMActionTests.kt new file mode 100644 index 0000000000..f2ed30c58d --- /dev/null +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/DefaultStartDMActionTests.kt @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.createroom.impl + +import androidx.compose.runtime.mutableStateOf +import com.google.common.truth.Truth.assertThat +import im.vector.app.features.analytics.plan.CreatedRoom +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_THROWABLE +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.services.analytics.api.AnalyticsService +import io.element.android.services.analytics.test.FakeAnalyticsService +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class DefaultStartDMActionTests { + + @Test + fun `when dm is found, assert state is updated with given room id`() = runTest { + val matrixClient = FakeMatrixClient().apply { + givenFindDmResult(A_ROOM_ID) + } + val action = createStartDMAction(matrixClient) + val state = mutableStateOf>(Async.Uninitialized) + action.execute(A_USER_ID, state) + assertThat(state.value).isEqualTo(Async.Success(A_ROOM_ID)) + } + + @Test + fun `when dm is not found, assert dm is created, state is updated with given room id and analytics get called`() = runTest { + val matrixClient = FakeMatrixClient().apply { + givenFindDmResult(null) + givenCreateDmResult(Result.success(A_ROOM_ID)) + } + val analyticsService = FakeAnalyticsService() + val action = createStartDMAction(matrixClient, analyticsService) + val state = mutableStateOf>(Async.Uninitialized) + action.execute(A_USER_ID, state) + assertThat(state.value).isEqualTo(Async.Success(A_ROOM_ID)) + assertThat(analyticsService.capturedEvents).containsExactly(CreatedRoom(isDM = true)) + } + + @Test + fun `when dm creation fails, assert state is updated with given error`() = runTest { + val matrixClient = FakeMatrixClient().apply { + givenFindDmResult(null) + givenCreateDmResult(Result.failure(A_THROWABLE)) + } + val action = createStartDMAction(matrixClient) + val state = mutableStateOf>(Async.Uninitialized) + action.execute(A_USER_ID, state) + assertThat(state.value).isEqualTo(Async.Failure(A_THROWABLE)) + } + + private fun createStartDMAction( + matrixClient: MatrixClient = FakeMatrixClient(), + analyticsService: AnalyticsService = FakeAnalyticsService(), + ): DefaultStartDMAction { + return DefaultStartDMAction( + matrixClient = matrixClient, + analyticsService = analyticsService, + ) + } +} diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt index bf4593bdbb..a0fb71dd31 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt @@ -20,25 +20,21 @@ import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import im.vector.app.features.analytics.plan.CreatedRoom +import io.element.android.features.createroom.api.StartDMAction import io.element.android.features.createroom.impl.userlist.FakeUserListPresenter import io.element.android.features.createroom.impl.userlist.FakeUserListPresenterFactory import io.element.android.features.createroom.impl.userlist.UserListDataStore -import io.element.android.features.createroom.impl.userlist.aUserListState +import io.element.android.features.createroom.test.FakeStartDMAction import io.element.android.libraries.architecture.Async import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_THROWABLE -import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.core.aBuildMeta -import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.usersearch.test.FakeUserRepository -import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.WarmUpRule -import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.test.runTest -import org.junit.Before import org.junit.Rule import org.junit.Test @@ -47,142 +43,57 @@ class CreateRoomRootPresenterTests { @get:Rule val warmUpRule = WarmUpRule() - private lateinit var userRepository: FakeUserRepository - private lateinit var presenter: CreateRoomRootPresenter - private lateinit var fakeUserListPresenter: FakeUserListPresenter - private lateinit var fakeMatrixClient: FakeMatrixClient - private lateinit var fakeAnalyticsService: FakeAnalyticsService - - @Before - fun setup() { - fakeUserListPresenter = FakeUserListPresenter() - fakeMatrixClient = FakeMatrixClient() - fakeAnalyticsService = FakeAnalyticsService() - userRepository = FakeUserRepository() - presenter = CreateRoomRootPresenter( - presenterFactory = FakeUserListPresenterFactory(fakeUserListPresenter), - userRepository = userRepository, - userListDataStore = UserListDataStore(), - matrixClient = fakeMatrixClient, - analyticsService = fakeAnalyticsService, - buildMeta = aBuildMeta(), - ) - } - @Test - fun `present - initial state`() = runTest { + fun `present - start DM action complete scenario`() = runTest { + val startDMAction = FakeStartDMAction() + val presenter = createCreateRoomRootPresenter(startDMAction) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() + assertThat(initialState.startDmAction).isInstanceOf(Async.Uninitialized::class.java) assertThat(initialState.applicationName).isEqualTo(aBuildMeta().applicationName) assertThat(initialState.userListState.selectedUsers).isEmpty() assertThat(initialState.userListState.isSearchActive).isFalse() assertThat(initialState.userListState.isMultiSelectionEnabled).isFalse() - } - } - @Test - fun `present - trigger create DM action`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() val matrixUser = MatrixUser(UserId("@name:domain")) - val createDmResult = Result.success(RoomId("!createDmResult:domain")) - - fakeMatrixClient.givenFindDmResult(null) - fakeMatrixClient.givenCreateDmResult(createDmResult) - - initialState.eventSink(CreateRoomRootEvents.StartDM(matrixUser)) - assertThat(awaitItem().startDmAction).isInstanceOf(Async.Loading::class.java) - val stateAfterStartDM = awaitItem() - assertThat(stateAfterStartDM.startDmAction).isInstanceOf(Async.Success::class.java) - assertThat(stateAfterStartDM.startDmAction.dataOrNull()).isEqualTo(createDmResult.getOrNull()) - } - } - - @Test - fun `present - creating a DM records analytics event`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() - val matrixUser = MatrixUser(UserId("@name:domain")) - val createDmResult = Result.success(RoomId("!createDmResult:domain")) - - fakeMatrixClient.givenFindDmResult(null) - fakeMatrixClient.givenCreateDmResult(createDmResult) - - initialState.eventSink(CreateRoomRootEvents.StartDM(matrixUser)) - skipItems(2) - - val analyticsEvent = fakeAnalyticsService.capturedEvents.filterIsInstance().firstOrNull() - assertThat(analyticsEvent).isNotNull() - assertThat(analyticsEvent?.isDM).isTrue() - } - } - - @Test - fun `present - trigger retrieve DM action`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() - val matrixUser = MatrixUser(UserId("@name:domain")) - val fakeDmResult = FakeMatrixRoom(roomId = RoomId("!fakeDmResult:domain")) - - fakeMatrixClient.givenFindDmResult(fakeDmResult) - - initialState.eventSink(CreateRoomRootEvents.StartDM(matrixUser)) - val stateAfterStartDM = awaitItem() - assertThat(stateAfterStartDM.startDmAction).isInstanceOf(Async.Success::class.java) - assertThat(stateAfterStartDM.startDmAction.dataOrNull()).isEqualTo(fakeDmResult.roomId) - assertThat(fakeAnalyticsService.capturedEvents.filterIsInstance()).isEmpty() - } - } - - @Test - fun `present - trigger retry create DM action`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() - val matrixUser = MatrixUser(UserId("@name:domain")) - val createDmResult = Result.success(RoomId("!createDmResult:domain")) - fakeUserListPresenter.givenState(aUserListState().copy(selectedUsers = persistentListOf(matrixUser))) - - fakeMatrixClient.givenFindDmResult(null) - fakeMatrixClient.givenCreateDmError(A_THROWABLE) - fakeMatrixClient.givenCreateDmResult(createDmResult) + val startDMSuccessResult = Async.Success(A_ROOM_ID) + val startDMFailureResult = Async.Failure(A_THROWABLE) // Failure + startDMAction.givenExecuteResult(startDMFailureResult) initialState.eventSink(CreateRoomRootEvents.StartDM(matrixUser)) assertThat(awaitItem().startDmAction).isInstanceOf(Async.Loading::class.java) - val stateAfterStartDM = awaitItem() - assertThat(stateAfterStartDM.startDmAction).isInstanceOf(Async.Failure::class.java) - assertThat(fakeAnalyticsService.capturedEvents.filterIsInstance()).isEmpty() + awaitItem().also { state -> + assertThat(state.startDmAction).isEqualTo(startDMFailureResult) + state.eventSink(CreateRoomRootEvents.CancelStartDM) + } - // Cancel - stateAfterStartDM.eventSink(CreateRoomRootEvents.CancelStartDM) - val stateAfterCancel = awaitItem() - assertThat(stateAfterCancel.startDmAction).isInstanceOf(Async.Uninitialized::class.java) - - // Failure - stateAfterCancel.eventSink(CreateRoomRootEvents.StartDM(matrixUser)) + // Success + startDMAction.givenExecuteResult(startDMSuccessResult) + awaitItem().also { state -> + assertThat(state.startDmAction).isEqualTo(Async.Uninitialized) + state.eventSink(CreateRoomRootEvents.StartDM(matrixUser)) + } assertThat(awaitItem().startDmAction).isInstanceOf(Async.Loading::class.java) - val stateAfterSecondAttempt = awaitItem() - assertThat(stateAfterSecondAttempt.startDmAction).isInstanceOf(Async.Failure::class.java) - assertThat(fakeAnalyticsService.capturedEvents.filterIsInstance()).isEmpty() + awaitItem().also { state -> + assertThat(state.startDmAction).isEqualTo(startDMSuccessResult) + } - // Retry with success - fakeMatrixClient.givenCreateDmError(null) - stateAfterSecondAttempt.eventSink(CreateRoomRootEvents.StartDM(matrixUser)) - assertThat(awaitItem().startDmAction).isInstanceOf(Async.Loading::class.java) - val stateAfterRetryStartDM = awaitItem() - assertThat(stateAfterRetryStartDM.startDmAction).isInstanceOf(Async.Success::class.java) - assertThat(stateAfterRetryStartDM.startDmAction.dataOrNull()).isEqualTo(createDmResult.getOrNull()) } } + + private fun createCreateRoomRootPresenter( + startDMAction: StartDMAction = FakeStartDMAction(), + ): CreateRoomRootPresenter { + return CreateRoomRootPresenter( + presenterFactory = FakeUserListPresenterFactory(FakeUserListPresenter()), + userRepository = FakeUserRepository(), + userListDataStore = UserListDataStore(), + startDMAction = startDMAction, + buildMeta = aBuildMeta(), + ) + } } diff --git a/libraries/theme/build.gradle.kts b/features/createroom/test/build.gradle.kts similarity index 68% rename from libraries/theme/build.gradle.kts rename to features/createroom/test/build.gradle.kts index 0c5c0b9548..53c3e6461a 100644 --- a/libraries/theme/build.gradle.kts +++ b/features/createroom/test/build.gradle.kts @@ -16,18 +16,16 @@ plugins { id("io.element.android-compose-library") - alias(libs.plugins.ksp) } android { - namespace = "io.element.android.libraries.theme" - - dependencies { - api(libs.androidx.compose.material3) - - ksp(libs.showkase.processor) - kspTest(libs.showkase.processor) - - implementation(libs.accompanist.systemui) - } + namespace = "io.element.android.features.createroom.test" +} + +dependencies { + implementation(libs.coroutines.core) + implementation(projects.libraries.matrix.api) + implementation(projects.libraries.matrix.test) + implementation(projects.libraries.architecture) + api(projects.features.createroom.api) } diff --git a/features/createroom/test/src/main/kotlin/io/element/android/features/createroom/test/FakeStartDMAction.kt b/features/createroom/test/src/main/kotlin/io/element/android/features/createroom/test/FakeStartDMAction.kt new file mode 100644 index 0000000000..c1888dc53f --- /dev/null +++ b/features/createroom/test/src/main/kotlin/io/element/android/features/createroom/test/FakeStartDMAction.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.createroom.test + +import androidx.compose.runtime.MutableState +import io.element.android.features.createroom.api.StartDMAction +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.test.A_ROOM_ID +import kotlinx.coroutines.delay + +class FakeStartDMAction : StartDMAction { + + private var executeResult: Async = Async.Success(A_ROOM_ID) + + fun givenExecuteResult(result: Async) { + executeResult = result + } + + override suspend fun execute(userId: UserId, actionState: MutableState>) { + actionState.value = Async.Loading() + delay(1) + actionState.value = executeResult + } +} diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt index 0b77d5e176..5a47e196df 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt @@ -20,7 +20,6 @@ import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.lifecycle.lifecycleScope -import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.navigation.backpresshandlerstrategies.BaseBackPressHandlerStrategy @@ -40,8 +39,8 @@ import io.element.android.features.ftue.impl.state.DefaultFtueState import io.element.android.features.ftue.impl.state.FtueStep import io.element.android.features.ftue.impl.welcome.WelcomeNode import io.element.android.features.lockscreen.api.LockScreenEntryPoint -import io.element.android.libraries.architecture.BackstackNode -import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler +import io.element.android.libraries.architecture.BackstackView +import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.SessionScope @@ -62,7 +61,7 @@ class FtueFlowNode @AssistedInject constructor( private val analyticsEntryPoint: AnalyticsEntryPoint, private val analyticsService: AnalyticsService, private val lockScreenEntryPoint: LockScreenEntryPoint, -) : BackstackNode( +) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Placeholder, savedStateMap = buildContext.savedStateMap, @@ -181,11 +180,7 @@ class FtueFlowNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { - Children( - navModel = backstack, - modifier = modifier, - transitionHandler = rememberDefaultTransitionHandler(), - ) + BackstackView() } @ContributesNode(AppScope::class) diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenView.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenView.kt index cf6c30434b..bf93aa2272 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenView.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenView.kt @@ -22,8 +22,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import io.element.android.features.ftue.impl.R import io.element.android.libraries.designsystem.atomic.pages.SunsetPage -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight @Composable fun MigrationScreenView( diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInView.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInView.kt index 95488d5b51..616e6aa809 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInView.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInView.kt @@ -36,6 +36,8 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.ftue.impl.R import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule @@ -43,13 +45,11 @@ import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.TextButton -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -79,7 +79,7 @@ private fun NotificationsOptInHeader( modifier = modifier, title = stringResource(R.string.screen_notification_optin_title), subTitle = stringResource(R.string.screen_notification_optin_subtitle), - iconResourceId = CommonDrawables.ic_compound_notifications_solid, + iconImageVector = CompoundIcons.NotificationsSolid, ) } diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/welcome/WelcomeView.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/welcome/WelcomeView.kt index c62c8bdaf2..def96af94e 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/welcome/WelcomeView.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/welcome/WelcomeView.kt @@ -31,6 +31,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.ftue.impl.R import io.element.android.libraries.designsystem.atomic.atoms.ElementLogoAtom import io.element.android.libraries.designsystem.atomic.atoms.ElementLogoAtomSize @@ -44,7 +46,6 @@ import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.persistentListOf @@ -105,7 +106,7 @@ private fun listItems() = persistentListOf( ), InfoListItem( message = stringResource(R.string.screen_welcome_bullet_3), - iconId = CommonDrawables.ic_compound_chat_problem, + iconVector = CompoundIcons.ChatProblem, ), ) diff --git a/features/ftue/impl/src/main/res/values-hu/translations.xml b/features/ftue/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..6585aaf4a8 --- /dev/null +++ b/features/ftue/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,13 @@ + + + "Ez egy egyszeri folyamat, köszönjük a türelmét." + "A fiók beállítása." + "A beállításokat később is módosíthatja." + "Értesítések engedélyezése, hogy soha ne maradjon le egyetlen üzenetről sem" + "A hívások, szavazások, keresések és egyebek az év további részében kerülnek hozzáadásra." + "A titkosított szobák üzenetelőzményei nem lesznek elérhetők ebben a frissítésben." + "Szeretnénk hallani a véleményét, ossza meg velünk a beállítások oldalon." + "Lássunk neki!" + "A következőket kell tudnia:" + "Üdvözli az %1$s!" + diff --git a/features/ftue/impl/src/main/res/values-in/translations.xml b/features/ftue/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..126ce6376b --- /dev/null +++ b/features/ftue/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,13 @@ + + + "Ini adalah proses satu kali, terima kasih telah menunggu." + "Menyiapkan akun Anda." + "Anda dapat mengubah pengaturan Anda nanti." + "Izinkan pemberitahuan dan jangan pernah melewatkan pesan" + "Panggilan, pemungutan suara, pencarian, dan lainnya akan ditambahkan di tahun ini." + "Riwayat pesan untuk ruangan terenkripsi tidak akan tersedia dalam pembaruan ini." + "Kami ingin mendengar dari Anda, beri tahu kami pendapat Anda melalui halaman pengaturan." + "Ayo!" + "Berikut adalah yang perlu Anda ketahui:" + "Selamat datang di %1$s!" + diff --git a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenterTests.kt b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenterTests.kt index db71128efd..e5c4d37423 100644 --- a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenterTests.kt +++ b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenterTests.kt @@ -20,7 +20,7 @@ import android.os.Build import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import io.element.android.libraries.permissions.api.PermissionStateProvider import io.element.android.libraries.permissions.api.PermissionsPresenter import io.element.android.libraries.permissions.impl.FakePermissionStateProvider @@ -52,7 +52,7 @@ class NotificationsOptInPresenterTests { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.notificationsPermissionState.showDialog).isFalse() + assertThat(initialState.notificationsPermissionState.showDialog).isFalse() } } @@ -65,7 +65,7 @@ class NotificationsOptInPresenterTests { }.test { val initialState = awaitItem() initialState.eventSink(NotificationsOptInEvents.ContinueClicked) - Truth.assertThat(awaitItem().notificationsPermissionState.showDialog).isTrue() + assertThat(awaitItem().notificationsPermissionState.showDialog).isTrue() } } @@ -80,7 +80,7 @@ class NotificationsOptInPresenterTests { }.test { val initialState = awaitItem() initialState.eventSink(NotificationsOptInEvents.ContinueClicked) - Truth.assertThat(isFinished).isTrue() + assertThat(isFinished).isTrue() } } @@ -96,7 +96,7 @@ class NotificationsOptInPresenterTests { }.test { val initialState = awaitItem() initialState.eventSink(NotificationsOptInEvents.NotNowClicked) - Truth.assertThat(isFinished).isTrue() + assertThat(isFinished).isTrue() } } @@ -122,7 +122,7 @@ class NotificationsOptInPresenterTests { val isPermissionDenied = runBlocking { permissionStateProvider.isPermissionDenied("notifications").first() } - Truth.assertThat(isPermissionDenied).isTrue() + assertThat(isPermissionDenied).isTrue() } } diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt index 648eb5094f..8bc752d629 100644 --- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt +++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt @@ -57,7 +57,7 @@ class InviteListPresenter @Inject constructor( override fun present(): InviteListState { val invites by client .roomListService - .invites() + .invites .summaries .collectAsState() diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt index d6d2d2be49..e661b121c9 100644 --- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt +++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt @@ -47,15 +47,15 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @Composable fun InviteListView( state: InviteListState, + onBackClicked: () -> Unit, + onInviteAccepted: (RoomId) -> Unit, modifier: Modifier = Modifier, - onBackClicked: () -> Unit = {}, - onInviteAccepted: (RoomId) -> Unit = {}, ) { if (state.acceptedAction is Async.Success) { LaunchedEffect(state.acceptedAction) { @@ -113,8 +113,8 @@ fun InviteListView( @Composable private fun InviteListContent( state: InviteListState, + onBackClicked: () -> Unit, modifier: Modifier = Modifier, - onBackClicked: () -> Unit = {}, ) { Scaffold( modifier = modifier, @@ -173,5 +173,9 @@ private fun InviteListContent( @PreviewsDayNight @Composable internal fun InviteListViewPreview(@PreviewParameter(InviteListStateProvider::class) state: InviteListState) = ElementPreview { - InviteListView(state) + InviteListView( + state = state, + onBackClicked = {}, + onInviteAccepted = {}, + ) } diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt index b48cea95da..193b78f594 100644 --- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt +++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt @@ -45,13 +45,13 @@ import io.element.android.features.invitelist.impl.model.InviteListInviteSummary import io.element.android.features.invitelist.impl.model.InviteSender import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAtom import io.element.android.libraries.designsystem.components.avatar.Avatar -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.ButtonSize import io.element.android.libraries.designsystem.theme.components.OutlinedButton import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings private val minHeight = 72.dp @@ -59,9 +59,9 @@ private val minHeight = 72.dp @Composable internal fun InviteSummaryRow( invite: InviteListInviteSummary, + onAcceptClicked: () -> Unit, + onDeclineClicked: () -> Unit, modifier: Modifier = Modifier, - onAcceptClicked: () -> Unit = {}, - onDeclineClicked: () -> Unit = {}, ) { Box( modifier = modifier @@ -79,8 +79,8 @@ internal fun InviteSummaryRow( @Composable private fun DefaultInviteSummaryRow( invite: InviteListInviteSummary, - onAcceptClicked: () -> Unit = {}, - onDeclineClicked: () -> Unit = {}, + onAcceptClicked: () -> Unit, + onDeclineClicked: () -> Unit, ) { Row( modifier = Modifier @@ -187,5 +187,9 @@ private fun SenderRow(sender: InviteSender) { @PreviewsDayNight @Composable internal fun InviteSummaryRowPreview(@PreviewParameter(InviteListInviteSummaryProvider::class) data: InviteListInviteSummary) = ElementPreview { - InviteSummaryRow(data) + InviteSummaryRow( + invite = data, + onAcceptClicked = {}, + onDeclineClicked = {}, + ) } diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/model/InviteListInviteSummary.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/model/InviteListInviteSummary.kt index cb695d4eda..9f74b90142 100644 --- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/model/InviteListInviteSummary.kt +++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/model/InviteListInviteSummary.kt @@ -33,7 +33,7 @@ data class InviteListInviteSummary( val isNew: Boolean = false, ) -data class InviteSender constructor( +data class InviteSender( val userId: UserId, val displayName: String, val avatarData: AvatarData = AvatarData(userId.value, displayName, size = AvatarSize.InviteSender), diff --git a/features/invitelist/impl/src/main/res/values-hu/translations.xml b/features/invitelist/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..e7faddd8f9 --- /dev/null +++ b/features/invitelist/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,9 @@ + + + "Biztos, hogy elutasítja a meghívást, hogy csatlakozzon ehhez: %1$s?" + "Meghívás elutasítása" + "Biztos, hogy elutasítja ezt a privát csevegést vele: %1$s?" + "Csevegés elutasítása" + "Nincsenek meghívások" + "%1$s (%2$s) meghívta" + diff --git a/features/invitelist/impl/src/main/res/values-in/translations.xml b/features/invitelist/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..97866a9043 --- /dev/null +++ b/features/invitelist/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,9 @@ + + + "Apakah Anda yakin ingin menolak undangan untuk bergabung ke %1$s?" + "Tolak undangan" + "Apakah Anda yakin ingin menolak obrolan pribadi dengan %1$s?" + "Tolak obrolan" + "Tidak ada undangan" + "%1$s (%2$s) mengundang Anda" + diff --git a/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt b/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt index ca0c73d53e..70908941a6 100644 --- a/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt +++ b/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt @@ -19,7 +19,7 @@ package io.element.android.features.invitelist.impl import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import io.element.android.features.invitelist.api.SeenInvitesStore import io.element.android.features.invitelist.test.FakeSeenInvitesStore import io.element.android.libraries.architecture.Async @@ -63,14 +63,14 @@ class InviteListPresenterTests { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.inviteList).isEmpty() + assertThat(initialState.inviteList).isEmpty() roomListService.postInviteRooms(listOf(aRoomSummary())) val withInviteState = awaitItem() - Truth.assertThat(withInviteState.inviteList.size).isEqualTo(1) - Truth.assertThat(withInviteState.inviteList[0].roomId).isEqualTo(A_ROOM_ID) - Truth.assertThat(withInviteState.inviteList[0].roomName).isEqualTo(A_ROOM_NAME) + assertThat(withInviteState.inviteList.size).isEqualTo(1) + assertThat(withInviteState.inviteList[0].roomId).isEqualTo(A_ROOM_ID) + assertThat(withInviteState.inviteList[0].roomName).isEqualTo(A_ROOM_NAME) } } @@ -84,11 +84,11 @@ class InviteListPresenterTests { presenter.present() }.test { val withInviteState = awaitItem() - Truth.assertThat(withInviteState.inviteList.size).isEqualTo(1) - Truth.assertThat(withInviteState.inviteList[0].roomId).isEqualTo(A_ROOM_ID) - Truth.assertThat(withInviteState.inviteList[0].roomAlias).isEqualTo(A_USER_ID.value) - Truth.assertThat(withInviteState.inviteList[0].roomName).isEqualTo(A_ROOM_NAME) - Truth.assertThat(withInviteState.inviteList[0].roomAvatarData).isEqualTo( + assertThat(withInviteState.inviteList.size).isEqualTo(1) + assertThat(withInviteState.inviteList[0].roomId).isEqualTo(A_ROOM_ID) + assertThat(withInviteState.inviteList[0].roomAlias).isEqualTo(A_USER_ID.value) + assertThat(withInviteState.inviteList[0].roomName).isEqualTo(A_ROOM_NAME) + assertThat(withInviteState.inviteList[0].roomAvatarData).isEqualTo( AvatarData( id = A_USER_ID.value, name = A_USER_NAME, @@ -96,7 +96,7 @@ class InviteListPresenterTests { size = AvatarSize.RoomInviteItem, ) ) - Truth.assertThat(withInviteState.inviteList[0].sender).isNull() + assertThat(withInviteState.inviteList[0].sender).isNull() } } @@ -110,10 +110,10 @@ class InviteListPresenterTests { presenter.present() }.test { val withInviteState = awaitItem() - Truth.assertThat(withInviteState.inviteList.size).isEqualTo(1) - Truth.assertThat(withInviteState.inviteList[0].sender?.displayName).isEqualTo(A_USER_NAME) - Truth.assertThat(withInviteState.inviteList[0].sender?.userId).isEqualTo(A_USER_ID) - Truth.assertThat(withInviteState.inviteList[0].sender?.avatarData).isEqualTo( + assertThat(withInviteState.inviteList.size).isEqualTo(1) + assertThat(withInviteState.inviteList[0].sender?.displayName).isEqualTo(A_USER_NAME) + assertThat(withInviteState.inviteList[0].sender?.userId).isEqualTo(A_USER_ID) + assertThat(withInviteState.inviteList[0].sender?.avatarData).isEqualTo( AvatarData( id = A_USER_ID.value, name = A_USER_NAME, @@ -142,11 +142,11 @@ class InviteListPresenterTests { originalState.eventSink(InviteListEvents.DeclineInvite(originalState.inviteList[0])) val newState = awaitItem() - Truth.assertThat(newState.declineConfirmationDialog).isInstanceOf(InviteDeclineConfirmationDialog.Visible::class.java) + assertThat(newState.declineConfirmationDialog).isInstanceOf(InviteDeclineConfirmationDialog.Visible::class.java) val confirmDialog = newState.declineConfirmationDialog as InviteDeclineConfirmationDialog.Visible - Truth.assertThat(confirmDialog.isDirect).isTrue() - Truth.assertThat(confirmDialog.name).isEqualTo(A_ROOM_NAME) + assertThat(confirmDialog.isDirect).isTrue() + assertThat(confirmDialog.name).isEqualTo(A_ROOM_NAME) } } @@ -163,11 +163,11 @@ class InviteListPresenterTests { originalState.eventSink(InviteListEvents.DeclineInvite(originalState.inviteList[0])) val newState = awaitItem() - Truth.assertThat(newState.declineConfirmationDialog).isInstanceOf(InviteDeclineConfirmationDialog.Visible::class.java) + assertThat(newState.declineConfirmationDialog).isInstanceOf(InviteDeclineConfirmationDialog.Visible::class.java) val confirmDialog = newState.declineConfirmationDialog as InviteDeclineConfirmationDialog.Visible - Truth.assertThat(confirmDialog.isDirect).isFalse() - Truth.assertThat(confirmDialog.name).isEqualTo(A_ROOM_NAME) + assertThat(confirmDialog.isDirect).isFalse() + assertThat(confirmDialog.name).isEqualTo(A_ROOM_NAME) } } @@ -188,7 +188,7 @@ class InviteListPresenterTests { originalState.eventSink(InviteListEvents.CancelDeclineInvite) val newState = awaitItem() - Truth.assertThat(newState.declineConfirmationDialog).isInstanceOf(InviteDeclineConfirmationDialog.Hidden::class.java) + assertThat(newState.declineConfirmationDialog).isInstanceOf(InviteDeclineConfirmationDialog.Hidden::class.java) } } @@ -215,7 +215,7 @@ class InviteListPresenterTests { skipItems(2) - Truth.assertThat(fakeNotificationDrawerManager.getClearMembershipNotificationForRoomCount(client.sessionId, A_ROOM_ID)).isEqualTo(1) + assertThat(fakeNotificationDrawerManager.getClearMembershipNotificationForRoomCount(client.sessionId, A_ROOM_ID)).isEqualTo(1) } } @@ -245,7 +245,7 @@ class InviteListPresenterTests { val newState = awaitItem() - Truth.assertThat(newState.declinedAction).isEqualTo(Async.Failure(ex)) + assertThat(newState.declinedAction).isEqualTo(Async.Failure(ex)) } } @@ -277,7 +277,7 @@ class InviteListPresenterTests { val newState = awaitItem() - Truth.assertThat(newState.declinedAction).isEqualTo(Async.Uninitialized) + assertThat(newState.declinedAction).isEqualTo(Async.Uninitialized) } } @@ -300,8 +300,8 @@ class InviteListPresenterTests { val newState = awaitItem() - Truth.assertThat(newState.acceptedAction).isEqualTo(Async.Success(A_ROOM_ID)) - Truth.assertThat(fakeNotificationDrawerManager.getClearMembershipNotificationForRoomCount(client.sessionId, A_ROOM_ID)).isEqualTo(1) + assertThat(newState.acceptedAction).isEqualTo(Async.Success(A_ROOM_ID)) + assertThat(fakeNotificationDrawerManager.getClearMembershipNotificationForRoomCount(client.sessionId, A_ROOM_ID)).isEqualTo(1) } } @@ -323,7 +323,7 @@ class InviteListPresenterTests { val originalState = awaitItem() originalState.eventSink(InviteListEvents.AcceptInvite(originalState.inviteList[0])) - Truth.assertThat(awaitItem().acceptedAction).isEqualTo(Async.Failure(ex)) + assertThat(awaitItem().acceptedAction).isEqualTo(Async.Failure(ex)) } } @@ -350,7 +350,7 @@ class InviteListPresenterTests { originalState.eventSink(InviteListEvents.DismissAcceptError) val newState = awaitItem() - Truth.assertThat(newState.acceptedAction).isEqualTo(Async.Uninitialized) + assertThat(newState.acceptedAction).isEqualTo(Async.Uninitialized) } } @@ -375,19 +375,19 @@ class InviteListPresenterTests { roomListService.postInviteRooms(listOf(aRoomSummary())) awaitItem() - Truth.assertThat(store.getProvidedRoomIds()).isEqualTo(setOf(A_ROOM_ID)) + assertThat(store.getProvidedRoomIds()).isEqualTo(setOf(A_ROOM_ID)) // When a second is added, both are saved roomListService.postInviteRooms(listOf(aRoomSummary(), aRoomSummary(A_ROOM_ID_2))) awaitItem() - Truth.assertThat(store.getProvidedRoomIds()).isEqualTo(setOf(A_ROOM_ID, A_ROOM_ID_2)) + assertThat(store.getProvidedRoomIds()).isEqualTo(setOf(A_ROOM_ID, A_ROOM_ID_2)) // When they're both dismissed, an empty set is saved roomListService.postInviteRooms(listOf()) awaitItem() - Truth.assertThat(store.getProvidedRoomIds()).isEmpty() + assertThat(store.getProvidedRoomIds()).isEmpty() } } @@ -413,11 +413,11 @@ class InviteListPresenterTests { skipItems(1) val withInviteState = awaitItem() - Truth.assertThat(withInviteState.inviteList.size).isEqualTo(2) - Truth.assertThat(withInviteState.inviteList[0].roomId).isEqualTo(A_ROOM_ID) - Truth.assertThat(withInviteState.inviteList[0].isNew).isFalse() - Truth.assertThat(withInviteState.inviteList[1].roomId).isEqualTo(A_ROOM_ID_2) - Truth.assertThat(withInviteState.inviteList[1].isNew).isTrue() + assertThat(withInviteState.inviteList.size).isEqualTo(2) + assertThat(withInviteState.inviteList[0].roomId).isEqualTo(A_ROOM_ID) + assertThat(withInviteState.inviteList[0].isNew).isFalse() + assertThat(withInviteState.inviteList[1].roomId).isEqualTo(A_ROOM_ID_2) + assertThat(withInviteState.inviteList[1].isNew).isTrue() } } diff --git a/features/leaveroom/api/src/main/res/values-hu/translations.xml b/features/leaveroom/api/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..824c088cdb --- /dev/null +++ b/features/leaveroom/api/src/main/res/values-hu/translations.xml @@ -0,0 +1,6 @@ + + + "Biztos, hogy elhagyja ezt a szobát? Ön az egyedüli ember itt. Ha kilép, akkor senki sem fog tudni csatlakozni a jövőben, Önt is beleértve." + "Biztos, hogy elhagyja ezt a szobát? Ez a szoba nem nyilvános, és meghívó nélkül nem fog tudni újra belépni." + "Biztos, hogy elhagyja a szobát?" + diff --git a/features/leaveroom/api/src/main/res/values-in/translations.xml b/features/leaveroom/api/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..682da2d6a1 --- /dev/null +++ b/features/leaveroom/api/src/main/res/values-in/translations.xml @@ -0,0 +1,6 @@ + + + "Apakah Anda yakin ingin meninggalkan ruangan ini? Anda adalah orang satu-satunya di sini. Jika Anda pergi, tidak akan ada yang bisa bergabung di masa depan, termasuk Anda." + "Apakah Anda yakin ingin meninggalkan ruangan ini? Ruangan ini tidak umum dan Anda tidak akan dapat bergabung kembali tanpa undangan." + "Apakah Anda yakin ingin meninggalkan ruangan?" + diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt index f4443e92f4..3aec28df82 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt @@ -34,14 +34,14 @@ import androidx.compose.ui.unit.dp import coil.compose.AsyncImagePainter import coil.compose.rememberAsyncImagePainter import coil.request.ImageRequest +import io.element.android.compound.theme.ElementTheme import io.element.android.features.location.api.internal.StaticMapPlaceholder import io.element.android.features.location.api.internal.StaticMapUrlBuilder import io.element.android.features.location.api.internal.centerBottomEdge -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme import timber.log.Timber /** diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt index ece32cbf5a..988f156dac 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt @@ -20,7 +20,7 @@ import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme /** * Builds a style URI for a MapLibre compatible tile server. diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/MapDefaults.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/MapDefaults.kt index 6fa74f04f0..f5f0bbe078 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/MapDefaults.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/MapDefaults.kt @@ -23,10 +23,10 @@ import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.graphics.Color import com.mapbox.mapboxsdk.camera.CameraPosition import com.mapbox.mapboxsdk.geometry.LatLng +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.maplibre.compose.MapLocationSettings import io.element.android.libraries.maplibre.compose.MapSymbolManagerSettings import io.element.android.libraries.maplibre.compose.MapUiSettings -import io.element.android.libraries.theme.ElementTheme /** * Common configuration values for the map. diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt index ccafa7e9e7..0d9befb26b 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt @@ -38,6 +38,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import com.mapbox.mapboxsdk.camera.CameraPosition +import io.element.android.compound.theme.ElementTheme import io.element.android.features.location.api.Location import io.element.android.features.location.api.internal.centerBottomEdge import io.element.android.features.location.api.internal.rememberTileStyleUrl @@ -61,15 +62,14 @@ import io.element.android.libraries.maplibre.compose.CameraMode import io.element.android.libraries.maplibre.compose.CameraMoveStartedReason import io.element.android.libraries.maplibre.compose.MapboxMap import io.element.android.libraries.maplibre.compose.rememberCameraPositionState -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @OptIn(ExperimentalMaterial3Api::class) @Composable fun SendLocationView( state: SendLocationState, + navigateUp: () -> Unit, modifier: Modifier = Modifier, - navigateUp: () -> Unit = {}, ) { LaunchedEffect(Unit) { state.eventSink(SendLocationEvents.RequestPermissions) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt index 82ac402011..999c2ee36a 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt @@ -36,13 +36,16 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import com.mapbox.mapboxsdk.camera.CameraPosition import com.mapbox.mapboxsdk.geometry.LatLng +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.compound.tokens.generated.TypographyTokens import io.element.android.features.location.api.internal.rememberTileStyleUrl import io.element.android.features.location.impl.common.MapDefaults import io.element.android.features.location.impl.common.PermissionDeniedDialog import io.element.android.features.location.impl.common.PermissionRationaleDialog import io.element.android.libraries.designsystem.components.button.BackButton -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.FloatingActionButton import io.element.android.libraries.designsystem.theme.components.Icon @@ -58,8 +61,6 @@ import io.element.android.libraries.maplibre.compose.MapboxMap import io.element.android.libraries.maplibre.compose.Symbol import io.element.android.libraries.maplibre.compose.rememberCameraPositionState import io.element.android.libraries.maplibre.compose.rememberSymbolState -import io.element.android.libraries.theme.ElementTheme -import io.element.android.libraries.theme.compound.generated.TypographyTokens import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.toImmutableMap @@ -67,8 +68,8 @@ import kotlinx.collections.immutable.toImmutableMap @Composable fun ShowLocationView( state: ShowLocationState, + onBackPressed: () -> Unit, modifier: Modifier = Modifier, - onBackPressed: () -> Unit = {}, ) { when (state.permissionDialog) { ShowLocationState.Dialog.None -> Unit @@ -125,7 +126,7 @@ fun ShowLocationView( actions = { IconButton(onClick = { state.eventSink(ShowLocationEvents.Share) }) { Icon( - resourceId = CommonDrawables.ic_compound_share_android, + imageVector = CompoundIcons.ShareAndroid, contentDescription = stringResource(CommonStrings.action_share), ) } diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt index 5b06090117..cfa52c4269 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt @@ -19,7 +19,7 @@ package io.element.android.features.location.impl.send import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.Composer import io.element.android.features.location.api.Location import io.element.android.features.location.impl.aPermissionsState @@ -77,16 +77,16 @@ class SendLocationPresenterTest { }.test { val initialState = awaitItem() - Truth.assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None) - Truth.assertThat(initialState.mode).isEqualTo(SendLocationState.Mode.SenderLocation) - Truth.assertThat(initialState.hasLocationPermission).isEqualTo(true) + assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None) + assertThat(initialState.mode).isEqualTo(SendLocationState.Mode.SenderLocation) + assertThat(initialState.hasLocationPermission).isTrue() // Swipe the map to switch mode initialState.eventSink(SendLocationEvents.SwitchToPinLocationMode) val myLocationState = awaitItem() - Truth.assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.None) - Truth.assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation) - Truth.assertThat(myLocationState.hasLocationPermission).isEqualTo(true) + assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.None) + assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation) + assertThat(myLocationState.hasLocationPermission).isTrue() } } @@ -104,16 +104,16 @@ class SendLocationPresenterTest { }.test { val initialState = awaitItem() - Truth.assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None) - Truth.assertThat(initialState.mode).isEqualTo(SendLocationState.Mode.SenderLocation) - Truth.assertThat(initialState.hasLocationPermission).isEqualTo(true) + assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None) + assertThat(initialState.mode).isEqualTo(SendLocationState.Mode.SenderLocation) + assertThat(initialState.hasLocationPermission).isTrue() // Swipe the map to switch mode initialState.eventSink(SendLocationEvents.SwitchToPinLocationMode) val myLocationState = awaitItem() - Truth.assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.None) - Truth.assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation) - Truth.assertThat(myLocationState.hasLocationPermission).isEqualTo(true) + assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.None) + assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation) + assertThat(myLocationState.hasLocationPermission).isTrue() } } @@ -130,16 +130,16 @@ class SendLocationPresenterTest { sendLocationPresenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None) - Truth.assertThat(initialState.mode).isEqualTo(SendLocationState.Mode.PinLocation) - Truth.assertThat(initialState.hasLocationPermission).isEqualTo(false) + assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None) + assertThat(initialState.mode).isEqualTo(SendLocationState.Mode.PinLocation) + assertThat(initialState.hasLocationPermission).isFalse() // Click on the button to switch mode initialState.eventSink(SendLocationEvents.SwitchToMyLocationMode) val myLocationState = awaitItem() - Truth.assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionDenied) - Truth.assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation) - Truth.assertThat(myLocationState.hasLocationPermission).isEqualTo(false) + assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionDenied) + assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation) + assertThat(myLocationState.hasLocationPermission).isFalse() } } @@ -156,16 +156,16 @@ class SendLocationPresenterTest { sendLocationPresenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None) - Truth.assertThat(initialState.mode).isEqualTo(SendLocationState.Mode.PinLocation) - Truth.assertThat(initialState.hasLocationPermission).isEqualTo(false) + assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None) + assertThat(initialState.mode).isEqualTo(SendLocationState.Mode.PinLocation) + assertThat(initialState.hasLocationPermission).isFalse() // Click on the button to switch mode initialState.eventSink(SendLocationEvents.SwitchToMyLocationMode) val myLocationState = awaitItem() - Truth.assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionRationale) - Truth.assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation) - Truth.assertThat(myLocationState.hasLocationPermission).isEqualTo(false) + assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionRationale) + assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation) + assertThat(myLocationState.hasLocationPermission).isFalse() } } @@ -187,16 +187,16 @@ class SendLocationPresenterTest { // Click on the button to switch mode initialState.eventSink(SendLocationEvents.SwitchToMyLocationMode) val myLocationState = awaitItem() - Truth.assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionRationale) - Truth.assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation) - Truth.assertThat(myLocationState.hasLocationPermission).isEqualTo(false) + assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionRationale) + assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation) + assertThat(myLocationState.hasLocationPermission).isFalse() // Dismiss the dialog myLocationState.eventSink(SendLocationEvents.DismissDialog) val dialogDismissedState = awaitItem() - Truth.assertThat(dialogDismissedState.permissionDialog).isEqualTo(SendLocationState.Dialog.None) - Truth.assertThat(dialogDismissedState.mode).isEqualTo(SendLocationState.Mode.PinLocation) - Truth.assertThat(dialogDismissedState.hasLocationPermission).isEqualTo(false) + assertThat(dialogDismissedState.permissionDialog).isEqualTo(SendLocationState.Dialog.None) + assertThat(dialogDismissedState.mode).isEqualTo(SendLocationState.Mode.PinLocation) + assertThat(dialogDismissedState.hasLocationPermission).isFalse() } } @@ -218,13 +218,13 @@ class SendLocationPresenterTest { // Click on the button to switch mode initialState.eventSink(SendLocationEvents.SwitchToMyLocationMode) val myLocationState = awaitItem() - Truth.assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionRationale) - Truth.assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation) - Truth.assertThat(myLocationState.hasLocationPermission).isEqualTo(false) + assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionRationale) + assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation) + assertThat(myLocationState.hasLocationPermission).isFalse() // Continue the dialog sends permission request to the permissions presenter myLocationState.eventSink(SendLocationEvents.RequestPermissions) - Truth.assertThat(permissionsPresenterFake.events.last()).isEqualTo(PermissionsEvents.RequestPermissions) + assertThat(permissionsPresenterFake.events.last()).isEqualTo(PermissionsEvents.RequestPermissions) } } @@ -246,16 +246,16 @@ class SendLocationPresenterTest { // Click on the button to switch mode initialState.eventSink(SendLocationEvents.SwitchToMyLocationMode) val myLocationState = awaitItem() - Truth.assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionDenied) - Truth.assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation) - Truth.assertThat(myLocationState.hasLocationPermission).isEqualTo(false) + assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionDenied) + assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation) + assertThat(myLocationState.hasLocationPermission).isFalse() // Dismiss the dialog myLocationState.eventSink(SendLocationEvents.DismissDialog) val dialogDismissedState = awaitItem() - Truth.assertThat(dialogDismissedState.permissionDialog).isEqualTo(SendLocationState.Dialog.None) - Truth.assertThat(dialogDismissedState.mode).isEqualTo(SendLocationState.Mode.PinLocation) - Truth.assertThat(dialogDismissedState.hasLocationPermission).isEqualTo(false) + assertThat(dialogDismissedState.permissionDialog).isEqualTo(SendLocationState.Dialog.None) + assertThat(dialogDismissedState.mode).isEqualTo(SendLocationState.Mode.PinLocation) + assertThat(dialogDismissedState.hasLocationPermission).isFalse() } } @@ -292,8 +292,8 @@ class SendLocationPresenterTest { delay(1) // Wait for the coroutine to finish - Truth.assertThat(fakeMatrixRoom.sentLocations.size).isEqualTo(1) - Truth.assertThat(fakeMatrixRoom.sentLocations.last()).isEqualTo( + assertThat(fakeMatrixRoom.sentLocations.size).isEqualTo(1) + assertThat(fakeMatrixRoom.sentLocations.last()).isEqualTo( SendLocationInvocation( body = "Location was shared at geo:3.0,4.0;u=5.0", geoUri = "geo:3.0,4.0;u=5.0", @@ -303,8 +303,8 @@ class SendLocationPresenterTest { ) ) - Truth.assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1) - Truth.assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo( + assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1) + assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo( Composer( inThread = false, isEditing = false, @@ -348,8 +348,8 @@ class SendLocationPresenterTest { delay(1) // Wait for the coroutine to finish - Truth.assertThat(fakeMatrixRoom.sentLocations.size).isEqualTo(1) - Truth.assertThat(fakeMatrixRoom.sentLocations.last()).isEqualTo( + assertThat(fakeMatrixRoom.sentLocations.size).isEqualTo(1) + assertThat(fakeMatrixRoom.sentLocations.last()).isEqualTo( SendLocationInvocation( body = "Location was shared at geo:0.0,1.0", geoUri = "geo:0.0,1.0", @@ -359,8 +359,8 @@ class SendLocationPresenterTest { ) ) - Truth.assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1) - Truth.assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo( + assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1) + assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo( Composer( inThread = false, isEditing = false, @@ -405,8 +405,8 @@ class SendLocationPresenterTest { delay(1) // Wait for the coroutine to finish - Truth.assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1) - Truth.assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo( + assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1) + assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo( Composer( inThread = false, isEditing = true, @@ -444,8 +444,8 @@ class SendLocationPresenterTest { dialogShownState.eventSink(SendLocationEvents.OpenAppSettings) val settingsOpenedState = awaitItem() - Truth.assertThat(settingsOpenedState.permissionDialog).isEqualTo(SendLocationState.Dialog.None) - Truth.assertThat(fakeLocationActions.openSettingsInvocationsCount).isEqualTo(1) + assertThat(settingsOpenedState.permissionDialog).isEqualTo(SendLocationState.Dialog.None) + assertThat(fakeLocationActions.openSettingsInvocationsCount).isEqualTo(1) } } @@ -455,7 +455,7 @@ class SendLocationPresenterTest { sendLocationPresenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.appName).isEqualTo("app name") + assertThat(initialState.appName).isEqualTo("app name") } } } diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt index 28beb77819..4db3755fbd 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt @@ -19,7 +19,7 @@ package io.element.android.features.location.impl.show import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import io.element.android.features.location.api.Location import io.element.android.features.location.impl.aPermissionsState import io.element.android.features.location.impl.common.actions.FakeLocationActions @@ -66,10 +66,10 @@ class ShowLocationPresenterTest { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.location).isEqualTo(location) - Truth.assertThat(initialState.description).isEqualTo(A_DESCRIPTION) - Truth.assertThat(initialState.hasLocationPermission).isEqualTo(false) - Truth.assertThat(initialState.isTrackMyLocation).isEqualTo(false) + assertThat(initialState.location).isEqualTo(location) + assertThat(initialState.description).isEqualTo(A_DESCRIPTION) + assertThat(initialState.hasLocationPermission).isFalse() + assertThat(initialState.isTrackMyLocation).isFalse() } } @@ -86,10 +86,10 @@ class ShowLocationPresenterTest { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.location).isEqualTo(location) - Truth.assertThat(initialState.description).isEqualTo(A_DESCRIPTION) - Truth.assertThat(initialState.hasLocationPermission).isEqualTo(false) - Truth.assertThat(initialState.isTrackMyLocation).isEqualTo(false) + assertThat(initialState.location).isEqualTo(location) + assertThat(initialState.description).isEqualTo(A_DESCRIPTION) + assertThat(initialState.hasLocationPermission).isFalse() + assertThat(initialState.isTrackMyLocation).isFalse() } } @@ -101,10 +101,10 @@ class ShowLocationPresenterTest { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.location).isEqualTo(location) - Truth.assertThat(initialState.description).isEqualTo(A_DESCRIPTION) - Truth.assertThat(initialState.hasLocationPermission).isEqualTo(true) - Truth.assertThat(initialState.isTrackMyLocation).isEqualTo(false) + assertThat(initialState.location).isEqualTo(location) + assertThat(initialState.description).isEqualTo(A_DESCRIPTION) + assertThat(initialState.hasLocationPermission).isTrue() + assertThat(initialState.isTrackMyLocation).isFalse() } } @@ -116,10 +116,10 @@ class ShowLocationPresenterTest { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.location).isEqualTo(location) - Truth.assertThat(initialState.description).isEqualTo(A_DESCRIPTION) - Truth.assertThat(initialState.hasLocationPermission).isEqualTo(true) - Truth.assertThat(initialState.isTrackMyLocation).isEqualTo(false) + assertThat(initialState.location).isEqualTo(location) + assertThat(initialState.description).isEqualTo(A_DESCRIPTION) + assertThat(initialState.hasLocationPermission).isTrue() + assertThat(initialState.isTrackMyLocation).isFalse() } } @@ -131,8 +131,8 @@ class ShowLocationPresenterTest { val initialState = awaitItem() initialState.eventSink(ShowLocationEvents.Share) - Truth.assertThat(fakeLocationActions.sharedLocation).isEqualTo(location) - Truth.assertThat(fakeLocationActions.sharedLabel).isEqualTo(A_DESCRIPTION) + assertThat(fakeLocationActions.sharedLocation).isEqualTo(location) + assertThat(fakeLocationActions.sharedLabel).isEqualTo(A_DESCRIPTION) } } @@ -144,23 +144,23 @@ class ShowLocationPresenterTest { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.hasLocationPermission).isEqualTo(true) - Truth.assertThat(initialState.isTrackMyLocation).isEqualTo(false) + assertThat(initialState.hasLocationPermission).isTrue() + assertThat(initialState.isTrackMyLocation).isFalse() initialState.eventSink(ShowLocationEvents.TrackMyLocation(true)) val trackMyLocationState = awaitItem() delay(1) - Truth.assertThat(trackMyLocationState.hasLocationPermission).isEqualTo(true) - Truth.assertThat(trackMyLocationState.isTrackMyLocation).isEqualTo(true) + assertThat(trackMyLocationState.hasLocationPermission).isTrue() + assertThat(trackMyLocationState.isTrackMyLocation).isTrue() // Swipe the map to switch mode initialState.eventSink(ShowLocationEvents.TrackMyLocation(false)) val trackLocationDisabledState = awaitItem() - Truth.assertThat(trackLocationDisabledState.permissionDialog).isEqualTo(ShowLocationState.Dialog.None) - Truth.assertThat(trackLocationDisabledState.isTrackMyLocation).isEqualTo(false) - Truth.assertThat(trackLocationDisabledState.hasLocationPermission).isEqualTo(true) + assertThat(trackLocationDisabledState.permissionDialog).isEqualTo(ShowLocationState.Dialog.None) + assertThat(trackLocationDisabledState.isTrackMyLocation).isFalse() + assertThat(trackLocationDisabledState.hasLocationPermission).isTrue() } } @@ -182,16 +182,16 @@ class ShowLocationPresenterTest { // Click on the button to switch mode initialState.eventSink(ShowLocationEvents.TrackMyLocation(true)) val trackLocationState = awaitItem() - Truth.assertThat(trackLocationState.permissionDialog).isEqualTo(ShowLocationState.Dialog.PermissionRationale) - Truth.assertThat(trackLocationState.isTrackMyLocation).isEqualTo(false) - Truth.assertThat(trackLocationState.hasLocationPermission).isEqualTo(false) + assertThat(trackLocationState.permissionDialog).isEqualTo(ShowLocationState.Dialog.PermissionRationale) + assertThat(trackLocationState.isTrackMyLocation).isFalse() + assertThat(trackLocationState.hasLocationPermission).isFalse() // Dismiss the dialog initialState.eventSink(ShowLocationEvents.DismissDialog) val dialogDismissedState = awaitItem() - Truth.assertThat(dialogDismissedState.permissionDialog).isEqualTo(ShowLocationState.Dialog.None) - Truth.assertThat(dialogDismissedState.isTrackMyLocation).isEqualTo(false) - Truth.assertThat(dialogDismissedState.hasLocationPermission).isEqualTo(false) + assertThat(dialogDismissedState.permissionDialog).isEqualTo(ShowLocationState.Dialog.None) + assertThat(dialogDismissedState.isTrackMyLocation).isFalse() + assertThat(dialogDismissedState.hasLocationPermission).isFalse() } } @@ -213,13 +213,13 @@ class ShowLocationPresenterTest { // Click on the button to switch mode initialState.eventSink(ShowLocationEvents.TrackMyLocation(true)) val trackLocationState = awaitItem() - Truth.assertThat(trackLocationState.permissionDialog).isEqualTo(ShowLocationState.Dialog.PermissionRationale) - Truth.assertThat(trackLocationState.isTrackMyLocation).isEqualTo(false) - Truth.assertThat(trackLocationState.hasLocationPermission).isEqualTo(false) + assertThat(trackLocationState.permissionDialog).isEqualTo(ShowLocationState.Dialog.PermissionRationale) + assertThat(trackLocationState.isTrackMyLocation).isFalse() + assertThat(trackLocationState.hasLocationPermission).isFalse() // Continue the dialog sends permission request to the permissions presenter trackLocationState.eventSink(ShowLocationEvents.RequestPermissions) - Truth.assertThat(permissionsPresenterFake.events.last()).isEqualTo(PermissionsEvents.RequestPermissions) + assertThat(permissionsPresenterFake.events.last()).isEqualTo(PermissionsEvents.RequestPermissions) } } @@ -241,16 +241,16 @@ class ShowLocationPresenterTest { // Click on the button to switch mode initialState.eventSink(ShowLocationEvents.TrackMyLocation(true)) val trackLocationState = awaitItem() - Truth.assertThat(trackLocationState.permissionDialog).isEqualTo(ShowLocationState.Dialog.PermissionDenied) - Truth.assertThat(trackLocationState.isTrackMyLocation).isEqualTo(false) - Truth.assertThat(trackLocationState.hasLocationPermission).isEqualTo(false) + assertThat(trackLocationState.permissionDialog).isEqualTo(ShowLocationState.Dialog.PermissionDenied) + assertThat(trackLocationState.isTrackMyLocation).isFalse() + assertThat(trackLocationState.hasLocationPermission).isFalse() // Dismiss the dialog initialState.eventSink(ShowLocationEvents.DismissDialog) val dialogDismissedState = awaitItem() - Truth.assertThat(dialogDismissedState.permissionDialog).isEqualTo(ShowLocationState.Dialog.None) - Truth.assertThat(dialogDismissedState.isTrackMyLocation).isEqualTo(false) - Truth.assertThat(dialogDismissedState.hasLocationPermission).isEqualTo(false) + assertThat(dialogDismissedState.permissionDialog).isEqualTo(ShowLocationState.Dialog.None) + assertThat(dialogDismissedState.isTrackMyLocation).isFalse() + assertThat(dialogDismissedState.hasLocationPermission).isFalse() } } @@ -276,8 +276,8 @@ class ShowLocationPresenterTest { dialogShownState.eventSink(ShowLocationEvents.OpenAppSettings) val settingsOpenedState = awaitItem() - Truth.assertThat(settingsOpenedState.permissionDialog).isEqualTo(ShowLocationState.Dialog.None) - Truth.assertThat(fakeLocationActions.openSettingsInvocationsCount).isEqualTo(1) + assertThat(settingsOpenedState.permissionDialog).isEqualTo(ShowLocationState.Dialog.None) + assertThat(fakeLocationActions.openSettingsInvocationsCount).isEqualTo(1) } } @@ -287,7 +287,7 @@ class ShowLocationPresenterTest { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.appName).isEqualTo("app name") + assertThat(initialState.appName).isEqualTo("app name") } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt index 4818a51bdc..76a967dfd7 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt @@ -19,7 +19,6 @@ package io.element.android.features.lockscreen.impl import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin @@ -32,9 +31,9 @@ import io.element.android.features.lockscreen.api.LockScreenEntryPoint import io.element.android.features.lockscreen.impl.settings.LockScreenSettingsFlowNode import io.element.android.features.lockscreen.impl.setup.LockScreenSetupFlowNode import io.element.android.features.lockscreen.impl.unlock.PinUnlockNode -import io.element.android.libraries.architecture.BackstackNode +import io.element.android.libraries.architecture.BackstackView +import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.NodeInputs -import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope import kotlinx.parcelize.Parcelize @@ -43,7 +42,7 @@ import kotlinx.parcelize.Parcelize class LockScreenFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, -) : BackstackNode( +) : BaseFlowNode( backstack = BackStack( initialElement = plugins.filterIsInstance(Inputs::class.java).first().initialNavTarget, savedStateMap = buildContext.savedStateMap, @@ -93,10 +92,6 @@ class LockScreenFlowNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { - Children( - navModel = backstack, - modifier = modifier, - transitionHandler = rememberDefaultTransitionHandler(), - ) + BackstackView() } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/components/PinEntryTextField.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/components/PinEntryTextField.kt index 019236aba7..f0d5942542 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/components/PinEntryTextField.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/components/PinEntryTextField.kt @@ -39,7 +39,7 @@ import io.element.android.features.lockscreen.impl.pin.model.PinEntry import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.pinDigitBg -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable fun PinEntryTextField( diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt index dec3699d07..1008ebdcf0 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt @@ -20,7 +20,6 @@ import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.lifecycle.lifecycleScope -import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node @@ -36,8 +35,8 @@ import io.element.android.features.lockscreen.impl.pin.DefaultPinCodeManagerCall import io.element.android.features.lockscreen.impl.pin.PinCodeManager import io.element.android.features.lockscreen.impl.setup.pin.SetupPinNode import io.element.android.features.lockscreen.impl.unlock.PinUnlockNode -import io.element.android.libraries.architecture.BackstackNode -import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler +import io.element.android.libraries.architecture.BackstackView +import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope import kotlinx.coroutines.flow.first @@ -49,7 +48,7 @@ class LockScreenSettingsFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, private val pinCodeManager: PinCodeManager, -) : BackstackNode( +) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Unknown, savedStateMap = buildContext.savedStateMap, @@ -132,10 +131,6 @@ class LockScreenSettingsFlowNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { - Children( - navModel = backstack, - modifier = modifier, - transitionHandler = rememberDefaultTransitionHandler(), - ) + BackstackView() } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsView.kt index 15254eb728..72ad733f4f 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsView.kt @@ -29,7 +29,7 @@ import io.element.android.libraries.designsystem.components.preferences.Preferen import io.element.android.libraries.designsystem.components.preferences.PreferenceText import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable fun LockScreenSettingsView( diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/LockScreenSetupFlowNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/LockScreenSetupFlowNode.kt index f612f3b82e..c9b96d76f2 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/LockScreenSetupFlowNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/LockScreenSetupFlowNode.kt @@ -19,7 +19,6 @@ package io.element.android.features.lockscreen.impl.setup import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node @@ -34,8 +33,8 @@ import io.element.android.features.lockscreen.impl.pin.DefaultPinCodeManagerCall import io.element.android.features.lockscreen.impl.pin.PinCodeManager import io.element.android.features.lockscreen.impl.setup.biometric.SetupBiometricNode import io.element.android.features.lockscreen.impl.setup.pin.SetupPinNode -import io.element.android.libraries.architecture.BackstackNode -import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler +import io.element.android.libraries.architecture.BackstackView +import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope import kotlinx.parcelize.Parcelize @@ -45,7 +44,7 @@ class LockScreenSetupFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, private val pinCodeManager: PinCodeManager, -) : BackstackNode( +) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Pin, savedStateMap = buildContext.savedStateMap, @@ -106,10 +105,6 @@ class LockScreenSetupFlowNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { - Children( - navModel = backstack, - modifier = modifier, - transitionHandler = rememberDefaultTransitionHandler(), - ) + BackstackView() } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt index fcca4985f5..325a41c393 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt @@ -68,7 +68,7 @@ import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.designsystem.utils.OnLifecycleEvent -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -300,7 +300,7 @@ private fun PinUnlockHeader( .size(32.dp), tint = ElementTheme.colors.iconPrimary, imageVector = Icons.Filled.Lock, - contentDescription = "", + contentDescription = null, ) } Spacer(modifier = Modifier.height(16.dp)) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/keypad/PinKeypad.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/keypad/PinKeypad.kt index 9db5cfe11a..a72483b13e 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/keypad/PinKeypad.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/keypad/PinKeypad.kt @@ -45,7 +45,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.text.toSp import io.element.android.libraries.designsystem.theme.components.Icon -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf diff --git a/features/lockscreen/impl/src/main/res/values-de/translations.xml b/features/lockscreen/impl/src/main/res/values-de/translations.xml index 9491e2e1a0..5ece7d2914 100644 --- a/features/lockscreen/impl/src/main/res/values-de/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-de/translations.xml @@ -1,4 +1,37 @@ + + "Du hast %1$d Versuch zu entsperren" + "Du hast %1$d Versuche zum Entsperren" + + + "Falsche PIN. Du hast %1$d weitere Chance" + "Falsche PIN. Du hast %1$d weitere Chancen" + + "biometrische Authentifizierung" + "biometrisches Entsperren" + "Mit Biometrie entsperren" + "PIN vergessen?" + "PIN-Code ändern" + "Biometrisches Entsperren zulassen" + "Pin entfernen" + "Bist du sicher, dass du die PIN entfernen willst?" + "PIN entfernen?" + "%1$s zulassen" + "Ich möchte diese PIN verwenden." + "Spare dir etwas Zeit und benutze %1$s, um die App jedes Mal zu entsperren" + "PIN wählen" + "PIN bestätigen" + "Aus Sicherheitsgründen kann dieser PIN-Code nicht verwendet werden." + "Bitte eine andere PIN verwenden." + "Sperre %1$s, mit einem PIN Code um den Zugriff auf Element X zu beschränken. + +Wähle etwas Einprägsames. Bei falscher Eingabe wirst du aus der App ausgeloggt." + "Bitte gib die gleiche PIN wie zuvor ein." + "Die PINs stimmen nicht überein" + "Um fortzufahren, musst du dich erneut anmelden und eine neue PIN erstellen" + "Du wirst abgemeldet" + "Biometrie verwenden" + "PIN verwenden" "Abmelden…" diff --git a/features/lockscreen/impl/src/main/res/values-hu/translations.xml b/features/lockscreen/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..5f456e7ff2 --- /dev/null +++ b/features/lockscreen/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,37 @@ + + + + "%1$d próbálkozása van a feloldáshoz" + "%1$d próbálkozása van a feloldáshoz" + + + "Hibás PIN-kód. Még %1$d próbálkozási lehetősége maradt." + "Hibás PIN-kód. Még %1$d próbálkozási lehetősége maradt." + + "biometrikus hitelesítés" + "biometrikus feloldás" + "Feloldás biometrikus adatokkal" + "Elfelejtette a PIN-kódot?" + "PIN-kód módosítása" + "Biometrikus feloldás engedélyezése" + "PIN-kód eltávolítása" + "Biztos, hogy eltávolítja a PIN-kódot?" + "PIN-kód eltávolítása?" + "A %1$s engedélyezése" + "Inkább PIN-kód használata" + "Spóroljon meg némi időt, és használja a %1$st az alkalmazás feloldásához" + "PIN-kód kiválasztása" + "PIN-kód megerősítése" + "Ezt biztonsági okokból nem választhatja PIN-kódként" + "Válasszon egy másik PIN-kódot" + "Az %1$s zárolása a csevegései nagyobb biztonsága érdekében. + +Válasszon valami megjegyezhetőt. Ha elfelejti a PIN-kódot, akkor ki lesz jelentkeztetve az alkalmazásból." + "Adja meg a PIN-kódját kétszer" + "A PIN-kódok nem egyeznek" + "A folytatáshoz újra be kell jelentkeznie, és létre kell hoznia egy új PIN-kódot" + "Kijelentkeztetésre kerül" + "Biometrikus adatok használata" + "PIN-kód használata" + "Kijelentkezés…" + diff --git a/features/lockscreen/impl/src/main/res/values-in/translations.xml b/features/lockscreen/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..a82fff5621 --- /dev/null +++ b/features/lockscreen/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,35 @@ + + + + "Anda memiliki %1$d percobaan lagi untuk membuka kunci" + + + "PIN salah. Anda memiliki %1$d percobaan lagi" + + "autentikasi biometrik" + "pembukaan biometrik" + "Buka kunci dengan biometrik" + "Lupa PIN?" + "Ubah kode PIN" + "Perbolehkan pembukaan biometrik" + "Hapus PIN" + "Apakah Anda yakin ingin menghapus PIN?" + "Hapus PIN?" + "Perbolehkan %1$s" + "Saya lebih suka menggunakan PIN" + "Hemat waktu Anda dan gunakan %1$s untuk membuka kunci aplikasi setiap kalinya" + "Pilih PIN" + "Konfirmasi PIN" + "Anda tidak dapat memilih PIN ini demi keamanan" + "Pilih PIN yang lain" + "Kunci %1$s untuk menambahkan keamanan tambahan pada percakapan Anda. + +Pilih sesuatu yang mudah untuk diingat. Jika Anda lupa PIN ini, Anda akan dikeluarkan dari aplikasi." + "Silakan masukkan PIN yang sama dua kali" + "PIN tidak cocok" + "Anda harus masuk ulang dan membuat PIN baru untuk melanjutkan" + "Anda sedang dikeluarkan" + "Gunakan biometrik" + "Gunakan PIN" + "Mengeluarkan dari akun…" + diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt index 021f0132a2..87d673066d 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt @@ -94,8 +94,8 @@ class PinUnlockPresenterTest { } awaitLastSequentialItem().also { state -> assertThat(state.remainingAttempts.dataOrNull()).isEqualTo(0) - assertThat(state.showSignOutPrompt).isEqualTo(true) - assertThat(state.isSignOutPromptCancellable).isEqualTo(false) + assertThat(state.showSignOutPrompt).isTrue() + assertThat(state.isSignOutPromptCancellable).isFalse() } } } @@ -112,16 +112,16 @@ class PinUnlockPresenterTest { state.eventSink(PinUnlockEvents.OnForgetPin) } awaitLastSequentialItem().also { state -> - assertThat(state.showSignOutPrompt).isEqualTo(true) - assertThat(state.isSignOutPromptCancellable).isEqualTo(true) + assertThat(state.showSignOutPrompt).isTrue() + assertThat(state.isSignOutPromptCancellable).isTrue() state.eventSink(PinUnlockEvents.ClearSignOutPrompt) } awaitLastSequentialItem().also { state -> - assertThat(state.showSignOutPrompt).isEqualTo(false) + assertThat(state.showSignOutPrompt).isFalse() state.eventSink(PinUnlockEvents.OnForgetPin) } awaitLastSequentialItem().also { state -> - assertThat(state.showSignOutPrompt).isEqualTo(true) + assertThat(state.showSignOutPrompt).isTrue() state.eventSink(PinUnlockEvents.SignOut) } consumeItemsUntilPredicate { state -> diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt index 59c85bedf8..cd17a7daf3 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt @@ -23,7 +23,6 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.lifecycleScope -import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node @@ -35,6 +34,7 @@ import com.bumble.appyx.navmodel.backstack.operation.singleTop import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +import io.element.android.compound.theme.ElementTheme import io.element.android.features.login.api.oidc.OidcAction import io.element.android.features.login.api.oidc.OidcActionFlow import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource @@ -47,14 +47,13 @@ import io.element.android.features.login.impl.screens.loginpassword.LoginFormSta import io.element.android.features.login.impl.screens.loginpassword.LoginPasswordNode import io.element.android.features.login.impl.screens.searchaccountprovider.SearchAccountProviderNode import io.element.android.features.login.impl.screens.waitlistscreen.WaitListNode -import io.element.android.libraries.architecture.BackstackNode +import io.element.android.libraries.architecture.BackstackView +import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.NodeInputs -import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.AppScope import io.element.android.libraries.matrix.api.auth.OidcDetails -import io.element.android.libraries.theme.ElementTheme import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize @@ -68,7 +67,7 @@ class LoginFlowNode @AssistedInject constructor( private val accountProviderDataSource: AccountProviderDataSource, private val defaultLoginUserStory: DefaultLoginUserStory, private val oidcActionFlow: OidcActionFlow, -) : BackstackNode( +) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.ConfirmAccountProvider, savedStateMap = buildContext.savedStateMap, @@ -217,11 +216,6 @@ class LoginFlowNode @AssistedInject constructor( accountProviderDataSource.reset() } } - Children( - navModel = backstack, - modifier = modifier, - // Animate transition to change server screen - transitionHandler = rememberDefaultTransitionHandler(), - ) + BackstackView() } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt index 2f6f736935..7184b2503c 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt @@ -30,16 +30,16 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.login.impl.R import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtomSize -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme /** * https://www.figma.com/file/o9p34zmiuEpZRyvZXJZAYL/FTUE?type=design&node-id=604-60817 @@ -74,7 +74,7 @@ fun AccountProviderView( } else { RoundedIconAtom( size = RoundedIconAtomSize.Medium, - resourceId = CommonDrawables.ic_compound_search, + imageVector = CompoundIcons.Search, tint = MaterialTheme.colorScheme.primary, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt index 98e9278405..3dd2409161 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt @@ -25,8 +25,8 @@ import io.element.android.features.login.impl.error.ChangeServerError import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight @Composable fun ChangeServerView( diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverData.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverData.kt index c1f0158605..64e4c11723 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverData.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverData.kt @@ -16,7 +16,7 @@ package io.element.android.features.login.impl.resolver -data class HomeserverData constructor( +data class HomeserverData( // The computed homeserver url, for which a wellknown file has been retrieved, or just a valid Url val homeserverUrl: String, // True if a wellknown file has been found and is valid. If false, it means that the [homeserverUrl] is valid diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt index 0d375fb2d6..8f5292b5a6 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt @@ -45,8 +45,8 @@ import io.element.android.features.login.impl.changeserver.ChangeServerEvents import io.element.android.features.login.impl.changeserver.ChangeServerView import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.components.button.BackButton -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.TopAppBar diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt index 22862c4467..22508dd3d6 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt @@ -36,8 +36,8 @@ import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMo import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.matrix.api.auth.OidcDetails diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt index 8a69cdf442..303ffdae52 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt @@ -50,6 +50,8 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.login.impl.R import io.element.android.features.login.impl.error.isWaitListError import io.element.android.features.login.impl.error.loginError @@ -58,8 +60,8 @@ import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubti import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.components.form.textFieldState -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton @@ -69,10 +71,8 @@ import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.theme.components.autofill import io.element.android.libraries.designsystem.theme.components.onTabOrEnterKeyFocusNext -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) @@ -218,7 +218,7 @@ private fun LoginForm( IconButton(onClick = { loginFieldState = "" }) { - Icon(resourceId = CommonDrawables.ic_compound_close, contentDescription = stringResource(CommonStrings.action_clear)) + Icon(imageVector = CompoundIcons.Close, contentDescription = stringResource(CommonStrings.action_clear)) } } } else null, @@ -251,12 +251,12 @@ private fun LoginForm( visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), trailingIcon = { val image = - if (passwordVisible) CommonDrawables.ic_compound_visibility_on else CommonDrawables.ic_compound_visibility_off + if (passwordVisible) CompoundIcons.VisibilityOn else CompoundIcons.VisibilityOff val description = if (passwordVisible) stringResource(CommonStrings.a11y_hide_password) else stringResource(CommonStrings.a11y_show_password) IconButton(onClick = { passwordVisible = !passwordVisible }) { - Icon(resourceId = image, description) + Icon(imageVector = image, description) } }, keyboardOptions = KeyboardOptions( diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt index 47dfe248b7..09e8661b77 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt @@ -48,6 +48,7 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.appconfig.AuthenticationConfig +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.login.impl.R import io.element.android.features.login.impl.accountprovider.AccountProvider import io.element.android.features.login.impl.accountprovider.AccountProviderView @@ -66,7 +67,6 @@ import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.OutlinedTextField import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.onTabOrEnterKeyFocusNext -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.CommonStrings @@ -103,7 +103,7 @@ fun SearchAccountProviderView( item { IconTitleSubtitleMolecule( modifier = Modifier.padding(top = 16.dp, bottom = 40.dp, start = 16.dp, end = 16.dp), - iconResourceId = CommonDrawables.ic_compound_search, + iconImageVector = CompoundIcons.Search, title = stringResource(id = R.string.screen_account_provider_form_title), subTitle = stringResource(id = R.string.screen_account_provider_form_subtitle), ) @@ -139,7 +139,7 @@ fun SearchAccountProviderView( eventSink(SearchAccountProviderEvents.UserInput("")) }) { Icon( - resourceId = CommonDrawables.ic_compound_close, + imageVector = CompoundIcons.Close, contentDescription = stringResource(CommonStrings.action_clear) ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt index 8a3f49fdc0..0e40a4827a 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt @@ -36,8 +36,8 @@ import io.element.android.features.login.impl.error.loginError import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.atomic.pages.SunsetPage import io.element.android.libraries.designsystem.components.dialogs.RetryDialog -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.designsystem.utils.OnLifecycleEvent diff --git a/features/login/impl/src/main/res/values-de/translations.xml b/features/login/impl/src/main/res/values-de/translations.xml index 0e757190a9..2603548cab 100644 --- a/features/login/impl/src/main/res/values-de/translations.xml +++ b/features/login/impl/src/main/res/values-de/translations.xml @@ -18,6 +18,7 @@ "Homeserver-URL" "Du kannst nur eine Verbindung zu einem vorhandenen Server herstellen, der Sliding Sync unterstützt. Dein Homeserver-Administrator muss das konfigurieren. %1$s" "Wie lautet die Adresse deines Servers?" + "Wähle deinen Server aus" "Dieses Konto wurde deaktiviert." "Falscher Benutzername und/oder Passwort" "Dies ist keine gültige Benutzerkennung. Erwartetes Format: \'@user:homeserver.org\'" diff --git a/features/login/impl/src/main/res/values-hu/translations.xml b/features/login/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..defe5e41a4 --- /dev/null +++ b/features/login/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,42 @@ + + + "Fiókszolgáltató módosítása" + "Matrix-kiszolgáló webcíme" + "Adjon meg egy keresési kifejezést vagy egy tartománycímet." + "Keresés egy cégre, közösségre vagy privát kiszolgálóra." + "Fiókszolgáltató keresése" + "Itt lesznek a beszélgetései – ahogyan egy e-mail-szolgáltatást is használna a levelei kezeléséhez." + "Hamarosan bejelentkezik ide: %s" + "Itt lesznek a beszélgetései – ahogyan egy e-mail-szolgáltatást is használna a levelei kezeléséhez." + "Hamarosan létrehoz egy fiókot itt: %s" + "A Matrix.org egy nagy, ingyenes kiszolgáló a nyilvános Matrix-hálózaton, a biztonságos, decentralizált kommunikáció érdekében, amelyet a Matrix.org Alapítvány üzemeltet." + "Egyéb" + "Másik fiókszolgáltató, például a saját privát kiszolgáló vagy egy munkahelyi fiók használata." + "Fiókszolgáltató módosítása" + "Nem sikerült elérni ezt a Matrix-kiszolgálót. Ellenőrizze, hogy helyesen adta-e meg a Matrix-kiszolgáló webcímét. Ha a webcím helyes, akkor további segítségért lépjen kapcsolatba a Matrix-kiszolgáló rendszergazdájával." + "A kiszolgáló jelenleg nem támogatja a Sliding sync protokollt." + "Matrix-kiszolgáló webcíme" + "Csak olyan meglévő kiszolgálóhoz csatlakozhat, amely támogatja a Sliding sync protokollt. Ezt a Matrix-kiszolgáló rendszergazdájának kell beállítania. %1$s" + "Mi a kiszolgálója címe?" + "Válassza ki a kiszolgálóját" + "Ez a fiók deaktiválva lett." + "Helytelen felhasználónév vagy jelszó" + "Ez nem érvényes felhasználóazonosító. A várt formátum: „@user:homeserver.org”" + "A kiválasztott Matrix-kiszolgáló nem támogatja a jelszavas vagy OIDC-alapú bejelentkezést. Lépjen kapcsolatba a kiszolgáló rendszergazdájával, vagy válasszon másik Matrix-kiszolgálót." + "Adja meg adatait" + "Örülünk, hogy visszatért!" + "Bejelentkezés ide: %1$s" + "Fiókszolgáltató módosítása" + "Egy privát kiszolgáló az Element alkalmazottai számára." + "A Matrix egy nyitott hálózat a biztonságos, decentralizált kommunikációhoz." + "Itt lesznek a beszélgetései – ahogyan egy e-mail-szolgáltatást is használna a levelei kezeléséhez." + "Hamarosan bejelentkezik ebbe: %1$s" + "Hamarosan létrehoz egy fiókot ezen: %1$s" + "Jelenleg nagy a kereslet a(z) %2$s oldalon futó %1$s iránt. Térjen vissza néhány nap múlva az alkalmazáshoz, és próbálja újra. + +Köszönjük a türelmét!" + "Már majdnem kész van." + "Bent van." + "A Matrix egy nyitott hálózat a biztonságos, decentralizált kommunikációhoz." + "Üdvözli az %1$s!" + diff --git a/features/login/impl/src/main/res/values-in/translations.xml b/features/login/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..6d4cb91baa --- /dev/null +++ b/features/login/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,42 @@ + + + "Ubah penyedia akun" + "Alamat homeserver" + "Masukkan istilah pencarian atau alamat domain." + "Cari perusahaan, komunitas, atau server pribadi." + "Cari penyedia akun" + "Di sinilah percakapan Anda akan berlangsung — sama seperti Anda menggunakan penyedia surel untuk menyimpan surel Anda." + "Anda akan masuk ke %s" + "Di sinilah percakapan Anda akan berlangsung — sama seperti Anda menggunakan penyedia surel untuk menyimpan surel Anda." + "Anda akan membuat akun di %s" + "Matrix.org adalah server besar dan gratis di jaringan Matrix publik untuk komunikasi yang aman dan terdesentralisasi, disediakan oleh Yayasan Matrix.org." + "Lainnya" + "Gunakan penyedia akun yang berbeda, seperti server pribadi Anda sendiri atau akun kerja." + "Ubah penyedia akun" + "Kami tidak dapat menjangkau server ini. Periksa apakah Anda telah memasukkan URL homeserver dengan benar. Jika URL sudah benar, hubungi administrator homeserver Anda untuk bantuan lebih lanjut." + "Server ini saat ini tidak mendukung sinkronisasi geser." + "URL Homeserver" + "Anda hanya dapat terhubung ke server yang ada yang mendukung sinkronisasi geser. Admin homeserver Anda perlu mengaturnya. %1$s" + "Apa alamat server Anda?" + "Pilih server Anda" + "Akun ini telah dinonaktifkan." + "Nama pengguna dan/atau kata sandi salah" + "Ini bukan pengenal pengguna yang valid. Format yang diharapkan: \'@pengguna:homeserver.org\'" + "Homeserver yang dipilih tidak mendukung log masuk kata sandi atau OIDC. Silakan hubungi admin Anda atau pilih homeserver yang lain." + "Masukkan detail Anda" + "Selamat datang kembali!" + "Masuk ke %1$s" + "Ubah penyedia akun" + "Server pribadi untuk karyawan Element." + "Matrix adalah jaringan terbuka untuk komunikasi yang aman dan terdesentralisasi." + "Di sinilah percakapan Anda akan berlangsung — sama seperti Anda menggunakan penyedia surel untuk menyimpan surel Anda." + "Anda akan masuk ke %1$s" + "Anda akan membuat akun di %1$s" + "Ada permintaan tinggi untuk %1$s di %2$s saat ini. Kembalilah ke aplikasi dalam beberapa hari dan coba lagi. + +Terima kasih atas kesabaran Anda!" + "Anda hampir selesai." + "Anda sudah masuk." + "Matrix adalah jaringan terbuka untuk komunikasi yang aman dan terdesentralisasi." + "Selamat datang di %1$s!" + diff --git a/features/logout/impl/src/main/AndroidManifest.xml b/features/logout/impl/src/main/AndroidManifest.xml index e9c0841b6b..86d497f107 100644 --- a/features/logout/impl/src/main/AndroidManifest.xml +++ b/features/logout/impl/src/main/AndroidManifest.xml @@ -15,6 +15,6 @@ ~ limitations under the License. --> - + diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt index ff76bde776..b8d8772104 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt @@ -28,9 +28,11 @@ import androidx.compose.runtime.setValue import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState +import io.element.android.libraries.core.bool.orTrue import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.BackupUploadState import io.element.android.libraries.matrix.api.encryption.EncryptionService import kotlinx.coroutines.CoroutineScope @@ -70,6 +72,19 @@ class LogoutPresenter @Inject constructor( isLastSession = encryptionService.isLastDevice().getOrNull() ?: false } + val backupState by encryptionService.backupStateStateFlow.collectAsState() + val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState() + + val doesBackupExistOnServerAction: MutableState> = remember { + mutableStateOf(Async.Uninitialized) + } + + LaunchedEffect(backupState) { + if (backupState == BackupState.UNKNOWN) { + getKeyBackupStatus(doesBackupExistOnServerAction) + } + } + fun handleEvents(event: LogoutEvents) { when (event) { is LogoutEvents.Logout -> { @@ -89,6 +104,9 @@ class LogoutPresenter @Inject constructor( return LogoutState( isLastSession = isLastSession, + backupState = backupState, + doesBackupExistOnServer = doesBackupExistOnServerAction.value.dataOrNull().orTrue(), + recoveryState = recoveryState, backupUploadState = backupUploadState, showConfirmationDialog = showLogoutDialog, logoutAction = logoutAction.value, @@ -96,6 +114,12 @@ class LogoutPresenter @Inject constructor( ) } + private fun CoroutineScope.getKeyBackupStatus(action: MutableState>) = launch { + suspend { + encryptionService.doesBackupExistOnServer().getOrThrow() + }.runCatchingUpdatingState(action) + } + private fun CoroutineScope.logout( logoutAction: MutableState>, ignoreSdkError: Boolean, diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutState.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutState.kt index 1672640ab7..abade32b96 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutState.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutState.kt @@ -17,10 +17,15 @@ package io.element.android.features.logout.impl import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.BackupUploadState +import io.element.android.libraries.matrix.api.encryption.RecoveryState data class LogoutState( val isLastSession: Boolean, + val backupState: BackupState, + val doesBackupExistOnServer: Boolean, + val recoveryState: RecoveryState, val backupUploadState: BackupUploadState, val showConfirmationDialog: Boolean, val logoutAction: Async, diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutStateProvider.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutStateProvider.kt index ddf0f30340..8c9482105d 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutStateProvider.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutStateProvider.kt @@ -18,7 +18,9 @@ package io.element.android.features.logout.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.BackupUploadState +import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.encryption.SteadyStateException open class LogoutStateProvider : PreviewParameterProvider { @@ -32,16 +34,26 @@ open class LogoutStateProvider : PreviewParameterProvider { aLogoutState(logoutAction = Async.Loading()), aLogoutState(logoutAction = Async.Failure(Exception("Failed to logout"))), aLogoutState(backupUploadState = BackupUploadState.SteadyException(SteadyStateException.Connection("No network"))), + // Last session no recovery + aLogoutState(isLastSession = true, recoveryState = RecoveryState.DISABLED), + // Last session no backup + aLogoutState(isLastSession = true, backupState = BackupState.UNKNOWN, doesBackupExistOnServer = false), ) } fun aLogoutState( isLastSession: Boolean = false, + backupState: BackupState = BackupState.ENABLED, + doesBackupExistOnServer: Boolean = true, + recoveryState: RecoveryState = RecoveryState.ENABLED, backupUploadState: BackupUploadState = BackupUploadState.Unknown, showConfirmationDialog: Boolean = false, logoutAction: Async = Async.Uninitialized, ) = LogoutState( isLastSession = isLastSession, + backupState = backupState, + doesBackupExistOnServer = doesBackupExistOnServer, + recoveryState = recoveryState, backupUploadState = backupUploadState, showConfirmationDialog = showConfirmationDialog, logoutAction = logoutAction, diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt index 5976ed47a0..d3d4805f82 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt @@ -25,9 +25,12 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.atomic.pages.FlowStepPage import io.element.android.libraries.designsystem.components.ProgressDialog @@ -40,11 +43,12 @@ import io.element.android.libraries.designsystem.theme.components.OutlinedButton import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.progressIndicatorTrackColor import io.element.android.libraries.designsystem.utils.CommonDrawables +import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.BackupUploadState +import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.encryption.SteadyStateException import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -61,7 +65,7 @@ fun LogoutView( onBackClicked = onBackClicked, title = title(state), subTitle = subtitle(state), - iconResourceId = CommonDrawables.ic_key, + iconVector = ImageVector.vectorResource(CommonDrawables.ic_key), modifier = modifier, content = { Content(state) }, buttons = { @@ -118,7 +122,15 @@ fun LogoutView( private fun title(state: LogoutState): String { return when { state.backupUploadState.isBackingUp() -> stringResource(id = R.string.screen_signout_key_backup_ongoing_title) - state.isLastSession -> stringResource(id = R.string.screen_signout_key_backup_disabled_title) + state.isLastSession -> { + if (state.recoveryState != RecoveryState.ENABLED) { + stringResource(id = R.string.screen_signout_recovery_disabled_title) + } else if (state.backupState == BackupState.UNKNOWN && state.doesBackupExistOnServer.not()) { + stringResource(id = R.string.screen_signout_key_backup_disabled_title) + } else { + stringResource(id = R.string.screen_signout_save_recovery_key_title) + } + } else -> stringResource(CommonStrings.action_signout) } } @@ -136,11 +148,10 @@ private fun subtitle(state: LogoutState): String? { private fun BackupUploadState.isBackingUp(): Boolean { return when (this) { - BackupUploadState.Unknown, BackupUploadState.Waiting, - is BackupUploadState.Uploading, - is BackupUploadState.CheckingIfUploadNeeded -> true + is BackupUploadState.Uploading -> true is BackupUploadState.SteadyException -> exception is SteadyStateException.Connection + BackupUploadState.Unknown, BackupUploadState.Done, BackupUploadState.Error -> false } diff --git a/features/logout/impl/src/main/res/values-de/translations.xml b/features/logout/impl/src/main/res/values-de/translations.xml index 2b86bd3a3d..b4368bfca7 100644 --- a/features/logout/impl/src/main/res/values-de/translations.xml +++ b/features/logout/impl/src/main/res/values-de/translations.xml @@ -2,4 +2,17 @@ "Bist du sicher, dass du dich abmelden willst?" "Abmelden…" + "Du bist dabei, dich von deiner letzten Sitzung abzumelden. Wenn du dich jetzt abmeldest, verlierst du den Zugriff auf deine verschlüsselten Nachrichten." + "Du hast das Backup ausgeschaltet" + "Deine Schlüssel wurden noch gesichert, als du offline gegangen bist. Stelle die Verbindung wieder her, damit deine Schlüssel gesichert werden können, bevor du dich abmeldest." + "Deine Schlüssel werden noch gesichert" + "Bitte warte, bis der Vorgang abgeschlossen ist, bevor du dich abmeldest." + "Deine Schlüssel werden noch gesichert" + "Du bist dabei, dich von deiner letzten Sitzung abzumelden. Wenn du dich jetzt abmeldest, verlierst du den Zugriff auf deine verschlüsselten Nachrichten." + "Wiederherstellung nicht eingerichtet" + "Du bist dabei, dich von deiner letzten Sitzung abzumelden. Wenn du dich jetzt abmeldest, verlierst du möglicherweise den Zugriff auf deine verschlüsselten Nachrichten." + "Hast du deinen Wiederherstellungsschlüssel gespeichert?" + "Abmelden" + "Abmelden" + "Abmelden" diff --git a/features/logout/impl/src/main/res/values-hu/translations.xml b/features/logout/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..124fa50468 --- /dev/null +++ b/features/logout/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,18 @@ + + + "Biztos, hogy kijelentkezik?" + "Kijelentkezés…" + "Arra készül, hogy kijelentkezzen az utolsó munkamenetéből is. Ha most kijelentkezik, akkor elveszti a hozzáférését a titkosított üzeneteihez." + "Kikapcsolta a biztonsági mentést" + "A kulcsai mentése során bontotta a kapcsolatot. Kapcsolódjon újra, hogy a kulcsai továbbra is mentésre kerüljenek mielőtt kijelentkezik." + "A kulcsai mentése még folyamatban van" + "Kijelentkezés előtt várja meg a befejezését." + "A kulcsai mentése még folyamatban van" + "Arra készül, hogy kijelentkezzen az utolsó munkamenetéből is. Ha most kijelentkezik, akkor elveszti a hozzáférését a titkosított üzeneteihez." + "A helyreállítás nincs beállítva" + "Arra készül, hogy kijelentkezzen az utolsó munkamenetéből is. Ha most kijelentkezik, akkor elveszítheti a hozzáférését a titkosított üzeneteihez." + "Mentette a helyreállítási kulcsát?" + "Kijelentkezés" + "Kijelentkezés" + "Kijelentkezés" + diff --git a/features/logout/impl/src/main/res/values-in/translations.xml b/features/logout/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..537ac904ec --- /dev/null +++ b/features/logout/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,18 @@ + + + "Apakah Anda yakin ingin keluar dari akun?" + "Mengeluarkan dari akun…" + "Anda akan keluar dari sesi terakhir Anda. Jika Anda keluar sekarang, Anda akan kehilangan akses ke pesan terenkripsi Anda." + "Anda telah menonaktifkan pencadangan" + "Kunci Anda masih dicadangkan saat Anda luring. Sambungkan kembali sehingga kunci Anda dapat dicadangkan sebelum keluar." + "Kunci Anda masih dicadangkan" + "Mohon tunggu hingga proses ini selesai sebelum keluar." + "Kunci Anda masih dicadangkan" + "Anda akan keluar dari sesi Anda yang terakhir. Jika Anda keluar sekarang, Anda akan kehilangan akses ke pesan terenkripsi Anda." + "Pemulihan belum disiapkan" + "Anda akan keluar dari sesi terakhir Anda. Jika Anda keluar sekarang, Anda mungkin kehilangan akses ke pesan terenkripsi Anda." + "Apakah Anda sudah menyimpan kunci pemulihan Anda?" + "Keluar dari akun" + "Keluar dari akun" + "Keluar dari akun" + diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt index 9b06e71ba3..75401c290a 100644 --- a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt +++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt @@ -24,8 +24,10 @@ import io.element.android.libraries.architecture.Async import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.BackupUploadState import io.element.android.libraries.matrix.api.encryption.EncryptionService +import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService @@ -50,6 +52,9 @@ class LogoutPresenterTest { }.test { val initialState = awaitLastSequentialItem() assertThat(initialState.isLastSession).isFalse() + assertThat(initialState.backupState).isEqualTo(BackupState.UNKNOWN) + assertThat(initialState.doesBackupExistOnServer).isTrue() + assertThat(initialState.recoveryState).isEqualTo(RecoveryState.UNKNOWN) assertThat(initialState.backupUploadState).isEqualTo(BackupUploadState.Unknown) assertThat(initialState.showConfirmationDialog).isFalse() assertThat(initialState.logoutAction).isEqualTo(Async.Uninitialized) @@ -93,14 +98,15 @@ class LogoutPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) val initialState = awaitItem() assertThat(initialState.isLastSession).isFalse() assertThat(initialState.backupUploadState).isEqualTo(BackupUploadState.Unknown) assertThat(initialState.showConfirmationDialog).isFalse() assertThat(initialState.logoutAction).isEqualTo(Async.Uninitialized) + skipItems(1) val waitingState = awaitItem() assertThat(waitingState.backupUploadState).isEqualTo(BackupUploadState.Waiting) + skipItems(1) val uploadingState = awaitItem() assertThat(uploadingState.backupUploadState).isEqualTo(BackupUploadState.Uploading(backedUpCount = 1, totalCount = 2)) val doneState = awaitItem() @@ -155,7 +161,8 @@ class LogoutPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitLastSequentialItem() + skipItems(1) + val initialState = awaitItem() initialState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false)) val confirmationState = awaitItem() assertThat(confirmationState.showConfirmationDialog).isTrue() @@ -164,6 +171,7 @@ class LogoutPresenterTest { val loadingState = awaitItem() assertThat(loadingState.showConfirmationDialog).isFalse() assertThat(loadingState.logoutAction).isInstanceOf(Async.Loading::class.java) + skipItems(1) val errorState = awaitItem() assertThat(errorState.logoutAction).isEqualTo(Async.Failure(A_THROWABLE)) errorState.eventSink.invoke(LogoutEvents.CloseDialogs) @@ -183,7 +191,8 @@ class LogoutPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitLastSequentialItem() + skipItems(1) + val initialState = awaitItem() initialState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false)) val confirmationState = awaitItem() assertThat(confirmationState.showConfirmationDialog).isTrue() @@ -192,6 +201,7 @@ class LogoutPresenterTest { val loadingState = awaitItem() assertThat(loadingState.showConfirmationDialog).isFalse() assertThat(loadingState.logoutAction).isInstanceOf(Async.Loading::class.java) + skipItems(1) val errorState = awaitItem() assertThat(errorState.logoutAction).isEqualTo(Async.Failure(A_THROWABLE)) errorState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = true)) diff --git a/features/messages/api/build.gradle.kts b/features/messages/api/build.gradle.kts index 9e890265ec..5e15d8f38d 100644 --- a/features/messages/api/build.gradle.kts +++ b/features/messages/api/build.gradle.kts @@ -15,7 +15,7 @@ */ plugins { - id("io.element.android-library") + id("io.element.android-compose-library") } android { diff --git a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/HtmlConverterProvider.kt b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/HtmlConverterProvider.kt new file mode 100644 index 0000000000..a248905086 --- /dev/null +++ b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/HtmlConverterProvider.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.api.timeline + +import androidx.compose.runtime.Composable +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.wysiwyg.utils.HtmlConverter + +interface HtmlConverterProvider { + + @Composable + fun Update(currentUserId: UserId) + + fun provide(): HtmlConverter +} diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts index 6faf07f916..21d39de816 100644 --- a/features/messages/impl/build.gradle.kts +++ b/features/messages/impl/build.gradle.kts @@ -23,6 +23,11 @@ plugins { android { namespace = "io.element.android.features.messages.impl" + testOptions { + unitTests { + isIncludeAndroidResources = true + } + } } anvil { @@ -48,10 +53,12 @@ dependencies { implementation(projects.libraries.dateformatter.api) implementation(projects.libraries.eventformatter.api) implementation(projects.libraries.mediapickers.api) + implementation(projects.libraries.mediaviewer.api) implementation(projects.libraries.featureflag.api) implementation(projects.libraries.mediaupload.api) implementation(projects.libraries.permissions.api) implementation(projects.libraries.preferences.api) + implementation(projects.libraries.roomselect.api) implementation(projects.libraries.voicerecorder.api) implementation(projects.libraries.mediaplayer.api) implementation(projects.libraries.uiUtils) @@ -67,6 +74,7 @@ dependencies { implementation(libs.vanniktech.blurhash) implementation(libs.telephoto.zoomableimage) implementation(libs.matrix.emojibase.bindings) + api(libs.matrix.richtexteditor.compose) testImplementation(libs.test.junit) testImplementation(libs.coroutines.test) @@ -87,7 +95,14 @@ dependencies { testImplementation(projects.libraries.textcomposer.test) testImplementation(projects.libraries.voicerecorder.test) testImplementation(projects.libraries.mediaplayer.test) + testImplementation(projects.libraries.mediaviewer.test) testImplementation(libs.test.mockk) + testImplementation(libs.test.junitext) + testImplementation(libs.test.robolectric) + testImplementation(projects.features.poll.test) + testImplementation(projects.features.poll.impl) + testImplementation(libs.androidx.compose.ui.test.junit) + testReleaseImplementation(libs.androidx.compose.ui.test.manifest) ksp(libs.showkase.processor) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index 128c531374..acccfeaedc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -20,9 +20,9 @@ import android.content.Context import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.node.node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack @@ -39,8 +39,6 @@ import io.element.android.features.messages.api.MessagesEntryPoint import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewNode import io.element.android.features.messages.impl.forward.ForwardMessagesNode -import io.element.android.features.messages.impl.media.local.MediaInfo -import io.element.android.features.messages.impl.media.viewer.MediaViewerNode import io.element.android.features.messages.impl.report.ReportMessageNode import io.element.android.features.messages.impl.timeline.debug.EventDebugInfoNode import io.element.android.features.messages.impl.timeline.model.TimelineItem @@ -50,9 +48,12 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent import io.element.android.features.poll.api.create.CreatePollEntryPoint -import io.element.android.libraries.architecture.BackstackNode -import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler +import io.element.android.features.poll.api.create.CreatePollMode +import io.element.android.libraries.architecture.BackstackWithOverlayBox +import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.architecture.overlay.Overlay +import io.element.android.libraries.architecture.overlay.operation.show import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.MatrixClient @@ -61,6 +62,8 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo +import io.element.android.libraries.mediaviewer.api.local.MediaInfo +import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode import kotlinx.collections.immutable.ImmutableList import kotlinx.parcelize.Parcelize @@ -73,16 +76,23 @@ class MessagesFlowNode @AssistedInject constructor( private val sendLocationEntryPoint: SendLocationEntryPoint, private val showLocationEntryPoint: ShowLocationEntryPoint, private val createPollEntryPoint: CreatePollEntryPoint, -) : BackstackNode( +) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Messages, savedStateMap = buildContext.savedStateMap, ), + overlay = Overlay( + savedStateMap = buildContext.savedStateMap, + ), buildContext = buildContext, plugins = plugins ) { sealed interface NavTarget : Parcelable { + + @Parcelize + data object Empty : NavTarget + @Parcelize data object Messages : NavTarget @@ -113,6 +123,9 @@ class MessagesFlowNode @AssistedInject constructor( @Parcelize data object CreatePoll : NavTarget + + @Parcelize + data class EditPoll(val eventId: EventId) : NavTarget } private val callback = plugins().firstOrNull() @@ -157,6 +170,10 @@ class MessagesFlowNode @AssistedInject constructor( backstack.push(NavTarget.CreatePoll) } + override fun onEditPollClicked(eventId: EventId) { + backstack.push(NavTarget.EditPoll(eventId)) + } + override fun onJoinCallClicked(roomId: RoomId) { val inputs = CallType.RoomCall( sessionId = matrixClient.sessionId, @@ -172,6 +189,8 @@ class MessagesFlowNode @AssistedInject constructor( mediaInfo = navTarget.mediaInfo, mediaSource = navTarget.mediaSource, thumbnailSource = navTarget.thumbnailSource, + canDownload = true, + canShare = true, ) createNode(buildContext, listOf(inputs)) } @@ -204,7 +223,17 @@ class MessagesFlowNode @AssistedInject constructor( sendLocationEntryPoint.createNode(this, buildContext) } NavTarget.CreatePoll -> { - createPollEntryPoint.createNode(this, buildContext) + createPollEntryPoint.nodeBuilder(this, buildContext) + .params(CreatePollEntryPoint.Params(mode = CreatePollMode.NewPoll)) + .build() + } + is NavTarget.EditPoll -> { + createPollEntryPoint.nodeBuilder(this, buildContext) + .params(CreatePollEntryPoint.Params(mode = CreatePollMode.EditPoll(eventId = navTarget.eventId))) + .build() + } + NavTarget.Empty -> { + node(buildContext) {} } } } @@ -222,10 +251,9 @@ class MessagesFlowNode @AssistedInject constructor( mediaSource = event.content.mediaSource, thumbnailSource = event.content.thumbnailSource, ) - backstack.push(navTarget) + overlay.show(navTarget) } is TimelineItemVideoContent -> { - val mediaSource = event.content.videoSource val navTarget = NavTarget.MediaViewer( mediaInfo = MediaInfo( name = event.content.body, @@ -233,13 +261,12 @@ class MessagesFlowNode @AssistedInject constructor( formattedFileSize = event.content.formattedFileSize, fileExtension = event.content.fileExtension ), - mediaSource = mediaSource, + mediaSource = event.content.videoSource, thumbnailSource = event.content.thumbnailSource, ) - backstack.push(navTarget) + overlay.show(navTarget) } is TimelineItemFileContent -> { - val mediaSource = event.content.fileSource val navTarget = NavTarget.MediaViewer( mediaInfo = MediaInfo( name = event.content.body, @@ -247,13 +274,12 @@ class MessagesFlowNode @AssistedInject constructor( formattedFileSize = event.content.formattedFileSize, fileExtension = event.content.fileExtension ), - mediaSource = mediaSource, + mediaSource = event.content.fileSource, thumbnailSource = event.content.thumbnailSource, ) - backstack.push(navTarget) + overlay.show(navTarget) } is TimelineItemAudioContent -> { - val mediaSource = event.content.mediaSource val navTarget = NavTarget.MediaViewer( mediaInfo = MediaInfo( name = event.content.body, @@ -261,17 +287,17 @@ class MessagesFlowNode @AssistedInject constructor( formattedFileSize = event.content.formattedFileSize, fileExtension = event.content.fileExtension ), - mediaSource = mediaSource, + mediaSource = event.content.mediaSource, thumbnailSource = null, ) - backstack.push(navTarget) + overlay.show(navTarget) } is TimelineItemLocationContent -> { val navTarget = NavTarget.LocationViewer( location = event.content.location, description = event.content.description, ) - backstack.push(navTarget) + overlay.show(navTarget) } else -> Unit } @@ -279,10 +305,6 @@ class MessagesFlowNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { - Children( - navModel = backstack, - modifier = modifier, - transitionHandler = rememberDefaultTransitionHandler(), - ) + BackstackWithOverlayBox(modifier) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt index a0517c59c4..cda8b7ddcd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt @@ -24,4 +24,5 @@ interface MessagesNavigator { fun onShowEventDebugInfoClicked(eventId: EventId?, debugInfo: TimelineItemDebugInfo) fun onForwardEventClicked(eventId: EventId) fun onReportContentClicked(eventId: EventId, senderId: UserId) + fun onEditPollClicked(eventId: EventId) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index 636569a24b..09883779e8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -66,6 +66,7 @@ class MessagesNode @AssistedInject constructor( fun onReportMessage(eventId: EventId, senderId: UserId) fun onSendLocationClicked() fun onCreatePollClicked() + fun onEditPollClicked(eventId: EventId) fun onJoinCallClicked(roomId: RoomId) } @@ -107,6 +108,10 @@ class MessagesNode @AssistedInject constructor( callback?.onReportMessage(eventId, senderId) } + override fun onEditPollClicked(eventId: EventId) { + callback?.onEditPollClicked(eventId) + } + private fun onSendLocationClicked() { callback?.onSendLocationClicked() } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index 2bb6e85df4..7d421a256a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -31,6 +31,7 @@ import androidx.compose.runtime.setValue import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import io.element.android.features.messages.api.timeline.HtmlConverterProvider import io.element.android.features.messages.impl.actionlist.ActionListEvents import io.element.android.features.messages.impl.actionlist.ActionListPresenter import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction @@ -79,6 +80,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.MessageEventType +import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType import io.element.android.libraries.matrix.ui.room.canRedactAsState @@ -93,7 +95,7 @@ class MessagesPresenter @AssistedInject constructor( private val room: MatrixRoom, private val composerPresenter: MessageComposerPresenter, private val voiceMessageComposerPresenter: VoiceMessageComposerPresenter, - private val timelinePresenter: TimelinePresenter, + timelinePresenterFactory: TimelinePresenter.Factory, private val actionListPresenter: ActionListPresenter, private val customReactionPresenter: CustomReactionPresenter, private val reactionSummaryPresenter: ReactionSummaryPresenter, @@ -106,10 +108,14 @@ class MessagesPresenter @AssistedInject constructor( private val clipboardHelper: ClipboardHelper, private val preferencesStore: PreferencesStore, private val featureFlagsService: FeatureFlagService, + private val htmlConverterProvider: HtmlConverterProvider, @Assisted private val navigator: MessagesNavigator, private val buildMeta: BuildMeta, + private val currentSessionIdHolder: CurrentSessionIdHolder, ) : Presenter { + private val timelinePresenter = timelinePresenterFactory.create(navigator = navigator) + @AssistedFactory interface Factory { fun create(navigator: MessagesNavigator): MessagesPresenter @@ -117,6 +123,8 @@ class MessagesPresenter @AssistedInject constructor( @Composable override fun present(): MessagesState { + htmlConverterProvider.Update(currentUserId = currentSessionIdHolder.current) + val roomInfo by room.roomInfoFlow.collectAsState(null) val localCoroutineScope = rememberCoroutineScope() val composerState = composerPresenter.present() @@ -142,6 +150,16 @@ class MessagesPresenter @AssistedInject constructor( mutableStateOf(false) } + var canJoinCall by rememberSaveable { + mutableStateOf(false) + } + + LaunchedEffect(currentSessionIdHolder.current) { + withContext(dispatchers.io) { + canJoinCall = room.canUserJoinCall(userId = currentSessionIdHolder.current).getOrDefault(false) + } + } + val inviteProgress = remember { mutableStateOf>(Async.Uninitialized) } var showReinvitePrompt by remember { mutableStateOf(false) } LaunchedEffect(hasDismissedInviteDialog, composerState.hasFocus, syncUpdateFlow) { @@ -160,8 +178,6 @@ class MessagesPresenter @AssistedInject constructor( val enableTextFormatting by preferencesStore.isRichTextEditorEnabledFlow().collectAsState(initial = true) var enableVoiceMessages by remember { mutableStateOf(false) } - // TODO add min power level to use this feature in the future? - val enableInRoomCalls = true LaunchedEffect(featureFlagsService) { enableVoiceMessages = featureFlagsService.isFeatureEnabled(FeatureFlags.VoiceMessages) } @@ -191,6 +207,12 @@ class MessagesPresenter @AssistedInject constructor( } } + val callState = when { + !canJoinCall -> RoomCallState.DISABLED + roomInfo?.hasRoomCall == true -> RoomCallState.ONGOING + else -> RoomCallState.ENABLED + } + return MessagesState( roomId = room.roomId, roomName = roomName, @@ -211,9 +233,8 @@ class MessagesPresenter @AssistedInject constructor( inviteProgress = inviteProgress.value, enableTextFormatting = enableTextFormatting, enableVoiceMessages = enableVoiceMessages, - enableInRoomCalls = enableInRoomCalls, appName = buildMeta.applicationName, - isCallOngoing = roomInfo?.hasRoomCall ?: false, + callState = callState, eventSink = { handleEvents(it) } ) } @@ -222,7 +243,7 @@ class MessagesPresenter @AssistedInject constructor( return AvatarData( id = id, name = name, - url = avatarUrl, + url = avatarUrl ?: room.avatarUrl, size = AvatarSize.TimelineRoom ) } @@ -294,20 +315,28 @@ class MessagesPresenter @AssistedInject constructor( composerState: MessageComposerState, enableTextFormatting: Boolean, ) { - val composerMode = MessageComposerMode.Edit( - targetEvent.eventId, - (targetEvent.content as? TimelineItemTextBasedContent)?.let { - if (enableTextFormatting) { - it.htmlBody ?: it.body - } else { - it.body - } - }.orEmpty(), - targetEvent.transactionId, - ) - composerState.eventSink( - MessageComposerEvents.SetMode(composerMode) - ) + when (targetEvent.content) { + is TimelineItemPollContent -> { + if (targetEvent.eventId == null) return + navigator.onEditPollClicked(targetEvent.eventId) + } + else -> { + val composerMode = MessageComposerMode.Edit( + targetEvent.eventId, + (targetEvent.content as? TimelineItemTextBasedContent)?.let { + if (enableTextFormatting) { + it.htmlBody ?: it.body + } else { + it.body + } + }.orEmpty(), + targetEvent.transactionId, + ) + composerState.eventSink( + MessageComposerEvents.SetMode(composerMode) + ) + } + } } private fun handleActionReply(targetEvent: TimelineItem.Event, composerState: MessageComposerState) { @@ -342,7 +371,10 @@ class MessagesPresenter @AssistedInject constructor( is TimelineItemLocationContent -> AttachmentThumbnailInfo( type = AttachmentThumbnailType.Location, ) - is TimelineItemPollContent, // TODO Polls: handle reply to + is TimelineItemPollContent -> AttachmentThumbnailInfo( + textContent = targetEvent.content.question, + type = AttachmentThumbnailType.Poll, + ) is TimelineItemTextBasedContent, is TimelineItemRedactedContent, is TimelineItemStateContent, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt index 325c695988..7d342ab107 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt @@ -51,8 +51,13 @@ data class MessagesState( val showReinvitePrompt: Boolean, val enableTextFormatting: Boolean, val enableVoiceMessages: Boolean, - val enableInRoomCalls: Boolean, - val isCallOngoing: Boolean, + val callState: RoomCallState, val appName: String, val eventSink: (MessagesEvents) -> Unit ) + +enum class RoomCallState { + ENABLED, + ONGOING, + DISABLED +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index 720c385f89..00c15fb410 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -66,7 +66,7 @@ open class MessagesStateProvider : PreviewParameterProvider { ), ), aMessagesState().copy( - isCallOngoing = true, + callState = RoomCallState.ONGOING, ), aMessagesState().copy( enableVoiceMessages = true, @@ -75,6 +75,9 @@ open class MessagesStateProvider : PreviewParameterProvider { showSendFailureDialog = true ), ), + aMessagesState().copy( + callState = RoomCallState.DISABLED, + ), ) } @@ -117,8 +120,7 @@ fun aMessagesState() = MessagesState( showReinvitePrompt = false, enableTextFormatting = true, enableVoiceMessages = true, - enableInRoomCalls = true, - isCallOngoing = false, + callState = RoomCallState.ENABLED, appName = "Element", eventSink = {} ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index c00fbe09aa..dd728aa274 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -57,6 +57,8 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.actionlist.ActionListEvents import io.element.android.features.messages.impl.actionlist.ActionListView import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction @@ -97,7 +99,6 @@ import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.designsystem.utils.KeepScreenOn import io.element.android.libraries.designsystem.utils.LogCompositions import io.element.android.libraries.designsystem.utils.OnLifecycleEvent @@ -105,7 +106,6 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList import timber.log.Timber @@ -190,10 +190,9 @@ fun MessagesView( MessagesViewTopBar( roomName = state.roomName.dataOrNull(), roomAvatar = state.roomAvatar.dataOrNull(), - inRoomCallsEnabled = state.enableInRoomCalls, + callState = state.callState, onBackPressed = onBackPressed, onRoomDetailsClicked = onRoomDetailsClicked, - isCallOngoing = state.isCallOngoing, onJoinCallClicked = onJoinCallClicked, ) } @@ -448,12 +447,11 @@ private fun MessagesViewComposerBottomSheetContents( private fun MessagesViewTopBar( roomName: String?, roomAvatar: AvatarData?, - inRoomCallsEnabled: Boolean, - isCallOngoing: Boolean, + callState: RoomCallState, + onRoomDetailsClicked: () -> Unit, + onJoinCallClicked: () -> Unit, + onBackPressed: () -> Unit, modifier: Modifier = Modifier, - onRoomDetailsClicked: () -> Unit = {}, - onJoinCallClicked: () -> Unit = {}, - onBackPressed: () -> Unit = {}, ) { TopAppBar( modifier = modifier, @@ -476,13 +474,14 @@ private fun MessagesViewTopBar( } }, actions = { - if (inRoomCallsEnabled) { - if (isCallOngoing) { - JoinCallMenuItem(onJoinCallClicked = onJoinCallClicked) - } else { - IconButton(onClick = onJoinCallClicked) { - Icon(CommonDrawables.ic_compound_video_call, contentDescription = stringResource(CommonStrings.a11y_start_call)) - } + if (callState == RoomCallState.ONGOING) { + JoinCallMenuItem(onJoinCallClicked = onJoinCallClicked) + } else { + IconButton(onClick = onJoinCallClicked, enabled = callState != RoomCallState.DISABLED) { + Icon( + imageVector = CompoundIcons.VideoCallSolid, + contentDescription = stringResource(CommonStrings.a11y_start_call), + ) } } Spacer(Modifier.width(8.dp)) @@ -507,7 +506,7 @@ private fun JoinCallMenuItem( ) { Icon( modifier = Modifier.size(20.dp), - resourceId = CommonDrawables.ic_compound_video_call, + imageVector = CompoundIcons.VideoCallSolid, contentDescription = null ) Spacer(Modifier.width(8.dp)) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt index cd11aa875b..d936ea9af8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt @@ -108,14 +108,14 @@ class ActionListPresenter @Inject constructor( is TimelineItemPollContent -> { buildList { val isMineOrCanRedact = timelineItem.isMine || userCanRedact - - // TODO Polls: Reply to poll. Ensure to update `fun TimelineItemEventContent.canBeReplied()` - // when touching this - // if (timelineItem.isRemote) { - // // Can only reply or forward messages already uploaded to the server - // add(TimelineItemAction.Reply) - // } - if (!timelineItem.content.isEnded && timelineItem.isRemote && isMineOrCanRedact) { + if (timelineItem.isRemote) { + // Can only reply or forward messages already uploaded to the server + add(TimelineItemAction.Reply) + } + if (timelineItem.isRemote && timelineItem.isEditable) { + add(TimelineItemAction.Edit) + } + if (timelineItem.isRemote && !timelineItem.content.isEnded && isMineOrCanRedact) { add(TimelineItemAction.EndPoll) } if (timelineItem.content.canBeCopied()) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt index 1e57de2466..c7b72ce527 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt @@ -34,7 +34,6 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme @@ -44,14 +43,16 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.clearAndSetSemantics +import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent @@ -82,10 +83,6 @@ import io.element.android.libraries.designsystem.theme.components.ModalBottomShe import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.hide import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail -import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo -import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList @@ -218,11 +215,8 @@ private fun SheetContent( @Composable private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modifier) { val content: @Composable () -> Unit - var icon: @Composable () -> Unit = { Avatar(avatarData = event.senderAvatar.copy(size = AvatarSize.MessageActionSender)) } + val icon: @Composable () -> Unit = { Avatar(avatarData = event.senderAvatar.copy(size = AvatarSize.MessageActionSender)) } val contentStyle = ElementTheme.typography.fontBodyMdRegular.copy(color = MaterialTheme.colorScheme.secondary) - val imageModifier = Modifier - .size(AvatarSize.MessageActionSender.dp) - .clip(RoundedCornerShape(9.dp)) @Composable fun ContentForBody(body: String) { @@ -234,87 +228,30 @@ private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modif val textContent = remember(event.content) { formatter.format(event) } when (event.content) { - is TimelineItemPollContent, // TODO Polls: handle summary is TimelineItemTextBasedContent, is TimelineItemStateContent, is TimelineItemEncryptedContent, is TimelineItemRedactedContent, is TimelineItemUnknownContent -> content = { ContentForBody(textContent) } is TimelineItemLocationContent -> { - icon = { - AttachmentThumbnail( - modifier = imageModifier, - info = AttachmentThumbnailInfo( - type = AttachmentThumbnailType.Location, - textContent = stringResource(CommonStrings.common_shared_location), - ) - ) - } content = { ContentForBody(stringResource(CommonStrings.common_shared_location)) } } is TimelineItemImageContent -> { - icon = { - AttachmentThumbnail( - modifier = imageModifier, - info = AttachmentThumbnailInfo( - thumbnailSource = event.content.thumbnailSource ?: event.content.mediaSource, - textContent = textContent, - type = AttachmentThumbnailType.Image, - blurHash = event.content.blurhash, - ) - ) - } content = { ContentForBody(event.content.body) } } is TimelineItemVideoContent -> { - icon = { - AttachmentThumbnail( - modifier = imageModifier, - info = AttachmentThumbnailInfo( - thumbnailSource = event.content.thumbnailSource, - textContent = textContent, - type = AttachmentThumbnailType.Video, - blurHash = event.content.blurHash, - ) - ) - } content = { ContentForBody(event.content.body) } } is TimelineItemFileContent -> { - icon = { - AttachmentThumbnail( - modifier = imageModifier, - info = AttachmentThumbnailInfo( - thumbnailSource = event.content.thumbnailSource, - textContent = textContent, - type = AttachmentThumbnailType.File, - ) - ) - } content = { ContentForBody(event.content.body) } } is TimelineItemAudioContent -> { - icon = { - AttachmentThumbnail( - modifier = imageModifier, - info = AttachmentThumbnailInfo( - textContent = textContent, - type = AttachmentThumbnailType.Audio, - ) - ) - } content = { ContentForBody(event.content.body) } } is TimelineItemVoiceContent -> { - icon = { - AttachmentThumbnail( - modifier = imageModifier, - info = AttachmentThumbnailInfo( - textContent = textContent, - type = AttachmentThumbnailType.Voice, - ) - ) - } + content = { ContentForBody(textContent) } + } + is TimelineItemPollContent -> { content = { ContentForBody(textContent) } } } @@ -371,7 +308,7 @@ private fun EmojiReactionsRow( ) { Icon( resourceId = CommonDrawables.ic_add_reaction, - contentDescription = "Emojis", + contentDescription = stringResource(id = CommonStrings.a11y_react_with_other_emojis), tint = MaterialTheme.colorScheme.secondary, modifier = Modifier .size(24.dp) @@ -398,11 +335,18 @@ private fun EmojiButton( } else { Color.Transparent } + val description = if (isHighlighted) { + stringResource(id = CommonStrings.a11y_remove_reaction_with, emoji) + } else { + stringResource(id = CommonStrings.a11y_react_with, emoji) + } Box( modifier = modifier .size(48.dp) - .background(backgroundColor, CircleShape), - + .background(backgroundColor, CircleShape) + .clearAndSetSemantics { + contentDescription = description + }, contentAlignment = Alignment.Center ) { Text( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemAction.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemAction.kt index d79b95ca70..70eb46844d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemAction.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemAction.kt @@ -19,6 +19,7 @@ package io.element.android.features.messages.impl.actionlist.model import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.compose.runtime.Immutable +import io.element.android.libraries.designsystem.icons.CompoundDrawables import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.ui.strings.CommonStrings @@ -30,11 +31,11 @@ sealed class TimelineItemAction( ) { data object Forward : TimelineItemAction(CommonStrings.action_forward, CommonDrawables.ic_forward) data object Copy : TimelineItemAction(CommonStrings.action_copy, CommonDrawables.ic_copy) - data object Redact : TimelineItemAction(CommonStrings.action_remove, CommonDrawables.ic_compound_delete, destructive = true) + data object Redact : TimelineItemAction(CommonStrings.action_remove, CompoundDrawables.ic_delete, destructive = true) data object Reply : TimelineItemAction(CommonStrings.action_reply, CommonDrawables.ic_reply) data object ReplyInThread : TimelineItemAction(CommonStrings.action_reply_in_thread, CommonDrawables.ic_reply) data object Edit : TimelineItemAction(CommonStrings.action_edit, CommonDrawables.ic_edit_outline) data object ViewSource : TimelineItemAction(CommonStrings.action_view_source, CommonDrawables.ic_developer_options) - data object ReportContent : TimelineItemAction(CommonStrings.action_report_content, CommonDrawables.ic_compound_chat_problem, destructive = true) - data object EndPoll : TimelineItemAction(CommonStrings.action_end_poll, CommonDrawables.ic_poll_end) + data object ReportContent : TimelineItemAction(CommonStrings.action_report_content, CompoundDrawables.ic_chat_problem, destructive = true) + data object EndPoll : TimelineItemAction(CommonStrings.action_end_poll, CompoundDrawables.ic_polls_end) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/Attachment.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/Attachment.kt index 8739a45201..55c8033636 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/Attachment.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/Attachment.kt @@ -18,7 +18,7 @@ package io.element.android.features.messages.impl.attachments import android.os.Parcelable import androidx.compose.runtime.Immutable -import io.element.android.features.messages.impl.media.local.LocalMedia +import io.element.android.libraries.mediaviewer.api.local.LocalMedia import kotlinx.parcelize.Parcelize @Immutable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt index a5dc90f02f..2ca04ecf19 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt @@ -24,10 +24,10 @@ import com.bumble.appyx.core.plugin.Plugin import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +import io.element.android.compound.theme.ForcedDarkElementTheme import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs -import io.element.android.libraries.theme.ForcedDarkElementTheme import io.element.android.libraries.di.RoomScope @ContributesNode(RoomScope::class) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt index 983d016854..28d403c9a6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt @@ -33,6 +33,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.isActive import kotlinx.coroutines.launch +import timber.log.Timber import kotlin.coroutines.coroutineContext class AttachmentsPreviewPresenter @AssistedInject constructor( @@ -114,6 +115,7 @@ class AttachmentsPreviewPresenter @AssistedInject constructor( sendActionState.value = SendActionState.Done }, onFailure = { error -> + Timber.e(error, "Failed to send attachment") if (error is CancellationException) { throw error } else { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt index de7f5cd47b..37b2ee9c78 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt @@ -19,10 +19,10 @@ package io.element.android.features.messages.impl.attachments.preview import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.core.net.toUri import io.element.android.features.messages.impl.attachments.Attachment -import io.element.android.features.messages.impl.media.local.LocalMedia -import io.element.android.features.messages.impl.media.local.MediaInfo -import io.element.android.features.messages.impl.media.local.aFileInfo -import io.element.android.features.messages.impl.media.local.anImageInfo +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.api.local.MediaInfo +import io.element.android.libraries.mediaviewer.api.local.aFileInfo +import io.element.android.libraries.mediaviewer.api.local.anImageInfo open class AttachmentsPreviewStateProvider : PreviewParameterProvider { override val values: Sequence diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt index 1bb8428e02..2e10eab9b6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt @@ -32,7 +32,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError -import io.element.android.features.messages.impl.media.local.LocalMediaView import io.element.android.libraries.designsystem.atomic.molecules.ButtonRowMolecule import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.designsystem.components.ProgressDialogType @@ -40,6 +39,7 @@ import io.element.android.libraries.designsystem.components.dialogs.RetryDialog import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.TextButton +import io.element.android.libraries.mediaviewer.api.local.LocalMediaView import io.element.android.libraries.ui.strings.CommonStrings @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesEvents.kt index f7058e95b3..dd8c4730bb 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesEvents.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesEvents.kt @@ -16,14 +16,6 @@ package io.element.android.features.messages.impl.forward -import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails - sealed interface ForwardMessagesEvents { - data class SetSelectedRoom(val room: RoomSummaryDetails) : ForwardMessagesEvents - // TODO remove to restore multi-selection - data object RemoveSelectedRoom : ForwardMessagesEvents - data object ToggleSearchActive : ForwardMessagesEvents - data class UpdateQuery(val query: String) : ForwardMessagesEvents - data object ForwardEvent : ForwardMessagesEvents data object ClearError : ForwardMessagesEvents } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesNode.kt index 13d26b9881..9d57f8e2fc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesNode.kt @@ -16,10 +16,15 @@ package io.element.android.features.messages.impl.forward +import android.os.Parcelable +import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.node.ParentNode import com.bumble.appyx.core.plugin.Plugin import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -29,14 +34,28 @@ import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.roomselect.api.RoomSelectEntryPoint +import io.element.android.libraries.roomselect.api.RoomSelectMode import kotlinx.collections.immutable.ImmutableList +import kotlinx.parcelize.Parcelize @ContributesNode(RoomScope::class) class ForwardMessagesNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, presenterFactory: ForwardMessagesPresenter.Factory, -) : Node(buildContext, plugins = plugins) { + private val roomSelectEntryPoint: RoomSelectEntryPoint, +) : ParentNode( + navModel = PermanentNavModel( + navTargets = setOf(NavTarget), + savedStateMap = buildContext.savedStateMap, + ), + buildContext = buildContext, + plugins = plugins, +) { + + @Parcelize + object NavTarget : Parcelable interface Callback : Plugin { fun onForwardedToSingleRoom(roomId: RoomId) @@ -48,6 +67,39 @@ class ForwardMessagesNode @AssistedInject constructor( private val presenter = presenterFactory.create(inputs.eventId.value) private val callbacks = plugins.filterIsInstance() + override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { + val callback = object : RoomSelectEntryPoint.Callback { + override fun onRoomSelected(roomIds: List) { + presenter.onRoomSelected(roomIds) + } + + override fun onCancel() { + navigateUp() + } + } + + return roomSelectEntryPoint.nodeBuilder(this, buildContext) + .callback(callback) + .params(RoomSelectEntryPoint.Params(mode = RoomSelectMode.Forward)) + .build() + } + + @Composable + override fun View(modifier: Modifier) { + Box(modifier = modifier) { + // Will render to room select screen + Children( + navModel = navModel, + ) + + val state = presenter.present() + ForwardMessagesView( + state = state, + onForwardingSucceeded = ::onSucceeded, + ) + } + } + private fun onSucceeded(roomIds: ImmutableList) { navigateUp() if (roomIds.size == 1) { @@ -55,15 +107,4 @@ class ForwardMessagesNode @AssistedInject constructor( callbacks.forEach { it.onForwardedToSingleRoom(targetRoomId) } } } - - @Composable - override fun View(modifier: Modifier) { - val state = presenter.present() - ForwardMessagesView( - state = state, - onDismiss = ::navigateUp, - onForwardingSucceeded = ::onSucceeded, - modifier = modifier - ) - } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenter.kt index 273ed5906d..f61f0f7b52 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenter.kt @@ -17,28 +17,20 @@ package io.element.android.features.messages.impl.forward import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.designsystem.theme.components.SearchBarResultState -import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.MatrixRoom -import io.element.android.libraries.matrix.api.roomlist.RoomSummary -import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -47,7 +39,6 @@ class ForwardMessagesPresenter @AssistedInject constructor( @Assisted eventId: String, private val room: MatrixRoom, private val matrixCoroutineScope: CoroutineScope, - private val client: MatrixClient, ) : Presenter { private val eventId: EventId = EventId(eventId) @@ -57,62 +48,25 @@ class ForwardMessagesPresenter @AssistedInject constructor( fun create(eventId: String): ForwardMessagesPresenter } + private val forwardingActionState: MutableState>> = mutableStateOf(Async.Uninitialized) + + fun onRoomSelected(roomIds: List) { + matrixCoroutineScope.forwardEvent(eventId, roomIds.toPersistentList(), forwardingActionState) + } + @Composable override fun present(): ForwardMessagesState { - var selectedRooms by remember { mutableStateOf(persistentListOf()) } - var query by remember { mutableStateOf("") } - var isSearchActive by remember { mutableStateOf(false) } - var results: SearchBarResultState> by remember { mutableStateOf(SearchBarResultState.NotSearching()) } - val forwardingActionState: MutableState>> = remember { mutableStateOf(Async.Uninitialized) } - - val summaries by client.roomListService.allRooms().summaries.collectAsState() - - LaunchedEffect(query, summaries) { - val filteredSummaries = summaries.filterIsInstance() - .map { it.details } - .filter { it.name.contains(query, ignoreCase = true) } - .distinctBy { it.roomId } // This should be removed once we're sure no duplicate Rooms can be received - .toPersistentList() - results = if (filteredSummaries.isNotEmpty()) { - SearchBarResultState.Results(filteredSummaries) - } else { - SearchBarResultState.NoResults() - } - } - val forwardingSucceeded by remember { derivedStateOf { forwardingActionState.value.dataOrNull() } } fun handleEvents(event: ForwardMessagesEvents) { when (event) { - is ForwardMessagesEvents.SetSelectedRoom -> { - selectedRooms = persistentListOf(event.room) - // Restore for multi-selection -// val index = selectedRooms.indexOfFirst { it.roomId == event.room.roomId } -// selectedRooms = if (index >= 0) { -// selectedRooms.removeAt(index) -// } else { -// selectedRooms.add(event.room) -// } - } - ForwardMessagesEvents.RemoveSelectedRoom -> selectedRooms = persistentListOf() - is ForwardMessagesEvents.UpdateQuery -> query = event.query - ForwardMessagesEvents.ToggleSearchActive -> isSearchActive = !isSearchActive - ForwardMessagesEvents.ForwardEvent -> { - isSearchActive = false - val roomIds = selectedRooms.map { it.roomId }.toPersistentList() - matrixCoroutineScope.forwardEvent(eventId, roomIds, forwardingActionState) - } ForwardMessagesEvents.ClearError -> forwardingActionState.value = Async.Uninitialized } } return ForwardMessagesState( - resultState = results, - query = query, - isSearchActive = isSearchActive, - selectedRooms = selectedRooms, isForwarding = forwardingActionState.value.isLoading(), error = (forwardingActionState.value as? Async.Failure)?.error, forwardingSucceeded = forwardingSucceeded, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesState.kt index 953a7897f6..d64c4dd7e9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesState.kt @@ -16,16 +16,11 @@ package io.element.android.features.messages.impl.forward -import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails import kotlinx.collections.immutable.ImmutableList data class ForwardMessagesState( - val resultState: SearchBarResultState>, - val query: String, - val isSearchActive: Boolean, - val selectedRooms: ImmutableList, + // TODO Migrate to an Async val isForwarding: Boolean, val error: Throwable?, val forwardingSucceeded: ImmutableList?, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt index dbcb4c8c3c..3029a174fc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt @@ -17,7 +17,6 @@ package io.element.android.features.messages.impl.forward import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.message.RoomMessage @@ -29,38 +28,13 @@ open class ForwardMessagesStateProvider : PreviewParameterProvider get() = sequenceOf( aForwardMessagesState(), - aForwardMessagesState(query = "Test", isSearchActive = true), - aForwardMessagesState(resultState = SearchBarResultState.Results(aForwardMessagesRoomList())), aForwardMessagesState( - resultState = SearchBarResultState.Results(aForwardMessagesRoomList()), - query = "Test", - isSearchActive = true, - ), - aForwardMessagesState( - resultState = SearchBarResultState.Results(aForwardMessagesRoomList()), - query = "Test", - isSearchActive = true, - selectedRooms = persistentListOf(aRoomDetailsState(roomId = RoomId("!room2:domain"))) - ), - aForwardMessagesState( - resultState = SearchBarResultState.Results(aForwardMessagesRoomList()), - query = "Test", - isSearchActive = true, - selectedRooms = persistentListOf(aRoomDetailsState(roomId = RoomId("!room2:domain"))), isForwarding = true, ), aForwardMessagesState( - resultState = SearchBarResultState.Results(aForwardMessagesRoomList()), - query = "Test", - isSearchActive = true, - selectedRooms = persistentListOf(aRoomDetailsState(roomId = RoomId("!room2:domain"))), forwardingSucceeded = persistentListOf(RoomId("!room2:domain")), ), aForwardMessagesState( - resultState = SearchBarResultState.Results(aForwardMessagesRoomList()), - query = "Test", - isSearchActive = true, - selectedRooms = persistentListOf(aRoomDetailsState(roomId = RoomId("!room2:domain"))), error = Throwable("error"), ), // Add other states here @@ -68,18 +42,10 @@ open class ForwardMessagesStateProvider : PreviewParameterProvider> = SearchBarResultState.NotSearching(), - query: String = "", - isSearchActive: Boolean = false, - selectedRooms: ImmutableList = persistentListOf(), isForwarding: Boolean = false, error: Throwable? = null, forwardingSucceeded: ImmutableList? = null, ) = ForwardMessagesState( - resultState = resultState, - query = query, - isSearchActive = isSearchActive, - selectedRooms = selectedRooms, isForwarding = isForwarding, error = error, forwardingSucceeded = forwardingSucceeded, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt index 1174c0ffcc..089046544b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt @@ -16,63 +16,20 @@ package io.element.android.features.messages.impl.forward -import androidx.activity.compose.BackHandler -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ExperimentalLayoutApi -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.consumeWindowInsets -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.heightIn -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.lazy.items -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.components.ProgressDialog -import io.element.android.libraries.designsystem.components.avatar.Avatar -import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.components.dialogs.ErrorDialogDefaults -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.theme.aliasScreenTitle -import io.element.android.libraries.designsystem.theme.components.HorizontalDivider -import io.element.android.libraries.designsystem.theme.components.RadioButton -import io.element.android.libraries.designsystem.theme.components.Scaffold -import io.element.android.libraries.designsystem.theme.components.SearchBar -import io.element.android.libraries.designsystem.theme.components.SearchBarResultState -import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.theme.components.TextButton -import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.designsystem.theme.roomListRoomMessage -import io.element.android.libraries.designsystem.theme.roomListRoomName +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails -import io.element.android.libraries.matrix.ui.components.SelectedRoom -import io.element.android.libraries.theme.ElementTheme -import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList -@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) @Composable fun ForwardMessagesView( state: ForwardMessagesState, - onDismiss: () -> Unit, onForwardingSucceeded: (ImmutableList) -> Unit, modifier: Modifier = Modifier, ) { @@ -81,193 +38,16 @@ fun ForwardMessagesView( return } - @Suppress("UNUSED_PARAMETER") - fun onRoomRemoved(roomSummaryDetails: RoomSummaryDetails) { - // TODO toggle selection when multi-selection is enabled - state.eventSink(ForwardMessagesEvents.RemoveSelectedRoom) + if (state.isForwarding) { + ProgressDialog(modifier) } - @Composable - fun SelectedRoomsHelper(isForwarding: Boolean, selectedRooms: ImmutableList) { - if (isForwarding) return - SelectedRooms( - selectedRooms = selectedRooms, - onRoomRemoved = ::onRoomRemoved, - modifier = Modifier.padding(vertical = 16.dp) + if (state.error != null) { + ForwardingErrorDialog( + modifier = modifier, + onDismiss = { state.eventSink(ForwardMessagesEvents.ClearError) }, ) } - - fun onBackButton(state: ForwardMessagesState) { - if (state.isSearchActive) { - state.eventSink(ForwardMessagesEvents.ToggleSearchActive) - } else { - onDismiss() - } - } - - BackHandler(onBack = { onBackButton(state) }) - - Scaffold( - modifier = modifier, - topBar = { - TopAppBar( - title = { - Text( - text = stringResource(CommonStrings.common_forward_message), - style = ElementTheme.typography.aliasScreenTitle - ) - }, - navigationIcon = { - BackButton(onClick = { onBackButton(state) }) - }, - actions = { - TextButton( - text = stringResource(CommonStrings.action_send), - enabled = state.selectedRooms.isNotEmpty(), - onClick = { state.eventSink(ForwardMessagesEvents.ForwardEvent) } - ) - } - ) - } - ) { paddingValues -> - Column( - Modifier - .padding(paddingValues) - .consumeWindowInsets(paddingValues) - ) { - SearchBar( - placeHolderTitle = stringResource(CommonStrings.action_search), - query = state.query, - onQueryChange = { state.eventSink(ForwardMessagesEvents.UpdateQuery(it)) }, - active = state.isSearchActive, - onActiveChange = { state.eventSink(ForwardMessagesEvents.ToggleSearchActive) }, - resultState = state.resultState, - showBackButton = false, - ) { summaries -> - LazyColumn { - item { - SelectedRoomsHelper( - isForwarding = state.isForwarding, - selectedRooms = state.selectedRooms - ) - } - items(summaries, key = { it.roomId.value }) { roomSummary -> - Column { - RoomSummaryView( - roomSummary, - isSelected = state.selectedRooms.any { it.roomId == roomSummary.roomId }, - onSelection = { roomSummary -> - state.eventSink(ForwardMessagesEvents.SetSelectedRoom(roomSummary)) - } - ) - HorizontalDivider(modifier = Modifier.fillMaxWidth()) - } - } - } - } - - if (!state.isSearchActive) { - // TODO restore for multi-selection -// SelectedRoomsHelper( -// isForwarding = state.isForwarding, -// selectedRooms = state.selectedRooms -// ) - Spacer(modifier = Modifier.height(20.dp)) - - if (state.resultState is SearchBarResultState.Results) { - LazyColumn { - items(state.resultState.results, key = { it.roomId.value }) { roomSummary -> - Column { - RoomSummaryView( - roomSummary, - isSelected = state.selectedRooms.any { it.roomId == roomSummary.roomId }, - onSelection = { roomSummary -> - state.eventSink(ForwardMessagesEvents.SetSelectedRoom(roomSummary)) - } - ) - HorizontalDivider(modifier = Modifier.fillMaxWidth()) - } - } - } - } - } - - if (state.isForwarding) { - ProgressDialog() - } - - if (state.error != null) { - ForwardingErrorDialog(onDismiss = { state.eventSink(ForwardMessagesEvents.ClearError) }) - } - } - } -} - -@Composable -private fun SelectedRooms( - selectedRooms: ImmutableList, - onRoomRemoved: (RoomSummaryDetails) -> Unit, - modifier: Modifier = Modifier, -) { - LazyRow( - modifier, - contentPadding = PaddingValues(horizontal = 16.dp), - horizontalArrangement = Arrangement.spacedBy(32.dp) - ) { - items(selectedRooms, key = { it.roomId.value }) { roomSummary -> - SelectedRoom(roomSummary = roomSummary, onRoomRemoved = onRoomRemoved) - } - } -} - -@Composable -private fun RoomSummaryView( - summary: RoomSummaryDetails, - isSelected: Boolean, - onSelection: (RoomSummaryDetails) -> Unit, - modifier: Modifier = Modifier -) { - Row( - modifier = modifier - .clickable { onSelection(summary) } - .fillMaxWidth() - .padding(start = 16.dp, end = 4.dp) - .heightIn(56.dp), - verticalAlignment = Alignment.CenterVertically - ) { - val roomAlias = summary.canonicalAlias ?: summary.roomId.value - Avatar( - avatarData = AvatarData( - id = roomAlias, - name = summary.name, - url = summary.avatarURLString, - size = AvatarSize.ForwardRoomListItem, - ), - ) - Column( - modifier = Modifier - .padding(start = 12.dp, end = 4.dp, top = 4.dp, bottom = 4.dp) - .weight(1f) - ) { - // Name - Text( - style = ElementTheme.typography.fontBodyLgRegular, - text = summary.name, - color = MaterialTheme.roomListRoomName(), - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - // Id - Text( - text = roomAlias, - color = MaterialTheme.roomListRoomMessage(), - style = ElementTheme.typography.fontBodySmRegular, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - } - RadioButton(selected = isSelected, onClick = { onSelection(summary) }) - } } @Composable @@ -284,7 +64,6 @@ private fun ForwardingErrorDialog(onDismiss: () -> Unit, modifier: Modifier = Mo internal fun ForwardMessagesViewPreview(@PreviewParameter(ForwardMessagesStateProvider::class) state: ForwardMessagesState) = ElementPreview { ForwardMessagesView( state = state, - onDismiss = {}, onForwardingSucceeded = {} ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/mentions/MentionSuggestionsPickerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/mentions/MentionSuggestionsPickerView.kt index 779398b020..1732b36069 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/mentions/MentionSuggestionsPickerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/mentions/MentionSuggestionsPickerView.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.features.messages.impl.R import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData @@ -42,7 +43,6 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembershipState -import io.element.android.libraries.theme.ElementTheme import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt index e29e64fae8..b3b53db013 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt @@ -32,6 +32,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.stringResource +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.R import io.element.android.libraries.androidutils.ui.hideKeyboard import io.element.android.libraries.designsystem.components.list.ListItemContent @@ -127,7 +128,7 @@ private fun AttachmentSourcePickerMenu( ) ListItem( modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.PickAttachmentSource.VideoFromCamera) }, - leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_video_call)), + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.VideoCall)), headlineContent = { Text(stringResource(R.string.screen_room_attachment_source_camera_video)) }, style = ListItemStyle.Primary, ) @@ -137,7 +138,7 @@ private fun AttachmentSourcePickerMenu( state.eventSink(MessageComposerEvents.PickAttachmentSource.Location) onSendLocationClicked() }, - leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_location_pin) ), + leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_location_pin)), headlineContent = { Text(stringResource(R.string.screen_room_attachment_source_location)) }, style = ListItemStyle.Primary, ) @@ -148,7 +149,7 @@ private fun AttachmentSourcePickerMenu( state.eventSink(MessageComposerEvents.PickAttachmentSource.Poll) onCreatePollClicked() }, - leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_polls)), + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Polls)), headlineContent = { Text(stringResource(R.string.screen_room_attachment_source_poll)) }, style = ListItemStyle.Primary, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index dd2537ff18..38ba51fa72 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -34,7 +34,6 @@ import androidx.media3.common.util.UnstableApi import im.vector.app.features.analytics.plan.Composer import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError -import io.element.android.features.messages.impl.media.local.LocalMediaFactory import io.element.android.features.messages.impl.mentions.MentionSuggestion import io.element.android.features.messages.impl.mentions.MentionSuggestionsProcessor import io.element.android.libraries.architecture.Presenter @@ -45,12 +44,14 @@ import io.element.android.libraries.di.SingleIn import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.core.ProgressCallback +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.Mention import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder import io.element.android.libraries.mediapickers.api.PickerProvider import io.element.android.libraries.mediaupload.api.MediaSender +import io.element.android.libraries.mediaviewer.api.local.LocalMediaFactory import io.element.android.libraries.permissions.api.PermissionsEvents import io.element.android.libraries.permissions.api.PermissionsPresenter import io.element.android.libraries.textcomposer.model.Message @@ -72,6 +73,7 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.merge import kotlinx.coroutines.isActive import kotlinx.coroutines.launch +import timber.log.Timber import javax.inject.Inject import kotlin.coroutines.coroutineContext import kotlin.time.Duration.Companion.seconds @@ -334,7 +336,7 @@ class MessageComposerPresenter @Inject constructor( add(Mention.AtRoom) } for (userId in state.userIds) { - add(Mention.User(userId)) + add(Mention.User(UserId(userId))) } } }.orEmpty() @@ -432,6 +434,7 @@ class MessageComposerPresenter @Inject constructor( attachmentState.value = AttachmentsState.None } .onFailure { cause -> + Timber.e(cause, "Failed to send attachment") attachmentState.value = AttachmentsState.None if (cause is CancellationException) { throw cause diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt index f4b152ecab..8ec2b8be03 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt @@ -40,6 +40,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.features.messages.impl.R import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.components.async.AsyncView @@ -52,7 +53,6 @@ import io.element.android.libraries.designsystem.theme.components.OutlinedTextFi import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/DefaultHtmlConverterProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/DefaultHtmlConverterProvider.kt new file mode 100644 index 0000000000..0afe57b9e5 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/DefaultHtmlConverterProvider.kt @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalInspectionMode +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.messages.api.timeline.HtmlConverterProvider +import io.element.android.libraries.core.bool.orFalse +import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.di.SingleIn +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.textcomposer.ElementRichTextEditorStyle +import io.element.android.libraries.textcomposer.mentions.rememberMentionSpanProvider +import io.element.android.wysiwyg.compose.StyledHtmlConverter +import io.element.android.wysiwyg.display.MentionDisplayHandler +import io.element.android.wysiwyg.display.TextDisplay +import io.element.android.wysiwyg.utils.HtmlConverter +import uniffi.wysiwyg_composer.newMentionDetector +import javax.inject.Inject + +@ContributesBinding(SessionScope::class) +@SingleIn(SessionScope::class) +class DefaultHtmlConverterProvider @Inject constructor(): HtmlConverterProvider { + + private val htmlConverter: MutableState = mutableStateOf(null) + + @Composable + override fun Update(currentUserId: UserId) { + val isInEditMode = LocalInspectionMode.current + val mentionDetector = remember(isInEditMode) { + if (isInEditMode) { null } else { newMentionDetector() } + } + + val editorStyle = ElementRichTextEditorStyle.textStyle() + val mentionSpanProvider = rememberMentionSpanProvider(currentUserId = currentUserId) + + val context = LocalContext.current + + htmlConverter.value = remember(editorStyle, mentionSpanProvider) { + StyledHtmlConverter( + context = context, + mentionDisplayHandler = object : MentionDisplayHandler { + override fun resolveAtRoomMentionDisplay(): TextDisplay { + return TextDisplay.Custom(mentionSpanProvider.getMentionSpanFor(text = "@room", url = "#")) + } + + override fun resolveMentionDisplay(text: String, url: String): TextDisplay { + return TextDisplay.Custom(mentionSpanProvider.getMentionSpanFor(text, url)) + } + }, + isMention = { _, url -> mentionDetector?.isMention(url).orFalse() } + ).apply { + configureWith(editorStyle) + } + } + } + + override fun provide(): HtmlConverter { + return htmlConverter.value ?: error("HtmlConverter wasn't instantiated. Make sure to call HtmlConverterProvider.Update() first.") + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt index ca23904583..2fdcb947b4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt @@ -30,4 +30,8 @@ sealed interface TimelineEvents { data class PollEndClicked( val pollStartId: EventId, ) : TimelineEvents + + data class PollEditClicked( + val pollStartId: EventId, + ) : TimelineEvents } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index 0bad8f8830..8613f2404e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -27,12 +27,17 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable -import im.vector.app.features.analytics.plan.PollEnd -import im.vector.app.features.analytics.plan.PollVote +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import io.element.android.features.messages.impl.MessagesNavigator import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory +import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.session.SessionState import io.element.android.features.messages.impl.voicemessages.timeline.RedactedVoiceMessageManager +import io.element.android.features.poll.api.actions.EndPollAction +import io.element.android.features.poll.api.actions.SendPollResponseAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.featureflag.api.FeatureFlagService @@ -43,34 +48,40 @@ import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.api.room.roomMembers +import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.matrix.ui.room.canSendMessageAsState -import io.element.android.services.analytics.api.AnalyticsService import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import javax.inject.Inject private const val BACK_PAGINATION_EVENT_LIMIT = 20 private const val BACK_PAGINATION_PAGE_SIZE = 50 -class TimelinePresenter @Inject constructor( +class TimelinePresenter @AssistedInject constructor( private val timelineItemsFactory: TimelineItemsFactory, private val room: MatrixRoom, private val dispatchers: CoroutineDispatchers, private val appScope: CoroutineScope, - private val analyticsService: AnalyticsService, + @Assisted private val navigator: MessagesNavigator, private val verificationService: SessionVerificationService, private val encryptionService: EncryptionService, private val featureFlagService: FeatureFlagService, private val redactedVoiceMessageManager: RedactedVoiceMessageManager, + private val sendPollResponseAction: SendPollResponseAction, + private val endPollAction: EndPollAction, ) : Presenter { + @AssistedFactory + interface Factory { + fun create(navigator: MessagesNavigator): TimelinePresenter + } + private val timeline = room.timeline @Composable @@ -89,7 +100,7 @@ class TimelinePresenter @Inject constructor( val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value) val prevMostRecentItemId = rememberSaveable { mutableStateOf(null) } - val hasNewItems = remember { mutableStateOf(false) } + val newItemState = remember { mutableStateOf(NewEventState.None) } val sessionVerifiedStatus by verificationService.sessionVerifiedStatus.collectAsState() val keyBackupState by encryptionService.backupStateStateFlow.collectAsState() @@ -112,7 +123,7 @@ class TimelinePresenter @Inject constructor( is TimelineEvents.SetHighlightedEvent -> highlightedEventId.value = event.eventId is TimelineEvents.OnScrollFinished -> { if (event.firstIndex == 0) { - hasNewItems.value = false + newItemState.value = NewEventState.None } appScope.sendReadReceiptIfNeeded( firstVisibleIndex = event.firstIndex, @@ -122,24 +133,23 @@ class TimelinePresenter @Inject constructor( ) } is TimelineEvents.PollAnswerSelected -> appScope.launch { - room.sendPollResponse( + sendPollResponseAction.execute( pollStartId = event.pollStartId, - answers = listOf(event.answerId), + answerId = event.answerId ) - analyticsService.capture(PollVote()) } is TimelineEvents.PollEndClicked -> appScope.launch { - room.endPoll( + endPollAction.execute( pollStartId = event.pollStartId, - text = "The poll with event id: ${event.pollStartId} has ended." ) - analyticsService.capture(PollEnd()) } + is TimelineEvents.PollEditClicked -> + navigator.onEditPollClicked(event.pollStartId) } } LaunchedEffect(timelineItems.size) { - computeHasNewItems(timelineItems, prevMostRecentItemId, hasNewItems) + computeNewItemState(timelineItems, prevMostRecentItemId, newItemState) } LaunchedEffect(Unit) { @@ -166,36 +176,50 @@ class TimelinePresenter @Inject constructor( } return TimelineState( + timelineRoomInfo = TimelineRoomInfo( + isDirect = room.isDirect + ), highlightedEventId = highlightedEventId.value, userHasPermissionToSendMessage = userHasPermissionToSendMessage, paginationState = paginationState, timelineItems = timelineItems, showReadReceipts = readReceiptsEnabled, - hasNewItems = hasNewItems.value, + newEventState = newItemState.value, sessionState = sessionState, - eventSink = ::handleEvents + eventSink = { handleEvents(it) } ) } /** * This method compute the hasNewItem state passed as a [MutableState] each time the timeline items size changes. * Basically, if we got new timeline event from sync or local, either from us or another user, we update the state so we tell we have new items. - * The state never goes back to false from this method, but need to be reset from somewhere else. + * The state never goes back to None from this method, but need to be reset from somewhere else. */ - private suspend fun computeHasNewItems( + private suspend fun computeNewItemState( timelineItems: ImmutableList, prevMostRecentItemId: MutableState, - hasNewItemsState: MutableState + newEventState: MutableState ) = withContext(dispatchers.computation) { + // FromMe is prioritized over FromOther, so skip if we already have a FromMe + if (newEventState.value == NewEventState.FromMe) { + return@withContext + } val newMostRecentItem = timelineItems.firstOrNull() val prevMostRecentItemIdValue = prevMostRecentItemId.value val newMostRecentItemId = newMostRecentItem?.identifier() - val hasNewItems = prevMostRecentItemIdValue != null && + val hasNewEvent = prevMostRecentItemIdValue != null && newMostRecentItem is TimelineItem.Event && newMostRecentItem.origin != TimelineItemEventOrigin.PAGINATION && newMostRecentItemId != prevMostRecentItemIdValue - if (hasNewItems) { - hasNewItemsState.value = true + if (hasNewEvent) { + val newMostRecentEvent = newMostRecentItem as? TimelineItem.Event + // Scroll to bottom if the new event is from me, even if sent from another device + val fromMe = newMostRecentEvent?.isMine == true + newEventState.value = if (fromMe) { + NewEventState.FromMe + } else { + NewEventState.FromOther + } } prevMostRecentItemId.value = newMostRecentItemId } @@ -211,7 +235,7 @@ class TimelinePresenter @Inject constructor( if (eventId != null && firstVisibleIndex <= lastReadReceiptIndex.value && eventId != lastReadReceiptId.value) { lastReadReceiptIndex.value = firstVisibleIndex lastReadReceiptId.value = eventId - timeline.sendReadReceipt(eventId) + timeline.sendReadReceipt(eventId = eventId, receiptType = ReceiptType.READ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt index 62836db130..d334aebf8c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt @@ -17,6 +17,7 @@ package io.element.android.features.messages.impl.timeline import androidx.compose.runtime.Immutable +import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.session.SessionState import io.element.android.libraries.matrix.api.core.EventId @@ -26,11 +27,17 @@ import kotlinx.collections.immutable.ImmutableList @Immutable data class TimelineState( val timelineItems: ImmutableList, + val timelineRoomInfo: TimelineRoomInfo, val showReadReceipts: Boolean, val highlightedEventId: EventId?, val userHasPermissionToSendMessage: Boolean, val paginationState: MatrixTimeline.PaginationState, - val hasNewItems: Boolean, + val newEventState: NewEventState, val sessionState: SessionState, val eventSink: (TimelineEvents) -> Unit ) + +@Immutable +data class TimelineRoomInfo( + val isDirect: Boolean, +) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt index 3defab97f5..63d8f0ed01 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt @@ -16,6 +16,9 @@ package io.element.android.features.messages.impl.timeline +import io.element.android.features.messages.impl.timeline.components.receipt.aReadReceiptData +import io.element.android.features.messages.impl.timeline.model.InReplyToDetails +import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.ReadReceiptData import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition @@ -34,7 +37,6 @@ import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo -import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -45,6 +47,7 @@ import kotlin.random.Random fun aTimelineState(timelineItems: ImmutableList = persistentListOf()) = TimelineState( timelineItems = timelineItems, + timelineRoomInfo = aTimelineRoomInfo(), showReadReceipts = false, paginationState = MatrixTimeline.PaginationState( isBackPaginating = false, @@ -53,7 +56,7 @@ fun aTimelineState(timelineItems: ImmutableList = persistentListOf ), highlightedEventId = null, userHasPermissionToSendMessage = true, - hasNewItems = false, + newEventState = NewEventState.None, sessionState = aSessionState( isSessionVerified = true, isKeyBackupEnabled = true, @@ -118,11 +121,12 @@ internal fun aTimelineItemEvent( eventId: EventId = EventId("\$" + Random.nextInt().toString()), transactionId: TransactionId? = null, isMine: Boolean = false, + isEditable: Boolean = false, senderDisplayName: String = "Sender", content: TimelineItemEventContent = aTimelineItemTextContent(), groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.None, sendState: LocalEventSendState? = null, - inReplyTo: InReplyTo? = null, + inReplyTo: InReplyToDetails? = null, isThreaded: Boolean = false, debugInfo: TimelineItemDebugInfo = aTimelineItemDebugInfo(), timelineItemReactions: TimelineItemReactions = aTimelineItemReactions(), @@ -139,6 +143,7 @@ internal fun aTimelineItemEvent( readReceiptState = readReceiptState, sentTime = "12:34", isMine = isMine, + isEditable = isEditable, senderDisplayName = senderDisplayName, groupPosition = groupPosition, localSendState = sendState, @@ -184,17 +189,33 @@ internal fun aTimelineItemReadReceipts(): TimelineItemReadReceipts { ) } -fun aGroupedEvents(id: Long = 0): TimelineItem.GroupedEvents { - val event = aTimelineItemEvent( +internal fun aGroupedEvents(id: Long = 0): TimelineItem.GroupedEvents { + val event1 = aTimelineItemEvent( isMine = true, content = aTimelineItemStateEventContent(), - groupPosition = TimelineItemGroupPosition.None + groupPosition = TimelineItemGroupPosition.None, + readReceiptState = TimelineItemReadReceipts( + receipts = listOf(aReadReceiptData(0)).toPersistentList(), + ), ) + val event2 = aTimelineItemEvent( + isMine = true, + content = aTimelineItemStateEventContent(body = "Another state event"), + groupPosition = TimelineItemGroupPosition.None, + readReceiptState = TimelineItemReadReceipts( + receipts = listOf(aReadReceiptData(1)).toPersistentList(), + ), + ) + val events = listOf(event1, event2) return TimelineItem.GroupedEvents( id = id.toString(), - events = listOf( - event, - event, - ).toImmutableList() + events = events.toImmutableList(), + aggregatedReadReceipts = events.flatMap { it.readReceiptState.receipts }.toImmutableList(), ) } + +internal fun aTimelineRoomInfo( + isDirect: Boolean = false, +) = TimelineRoomInfo( + isDirect = isDirect, +) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index 1d831e5e21..bc285b0bc5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -20,13 +20,11 @@ package io.element.android.features.messages.impl.timeline import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.animation.animateContentSize import androidx.compose.animation.core.tween import androidx.compose.animation.scaleIn import androidx.compose.animation.scaleOut import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding @@ -42,42 +40,35 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.rotate import androidx.compose.ui.platform.LocalInspectionMode -import androidx.compose.ui.res.pluralStringResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import io.element.android.features.messages.impl.R -import io.element.android.features.messages.impl.timeline.components.TimelineItemEventRow -import io.element.android.features.messages.impl.timeline.components.TimelineItemStateEventRow -import io.element.android.features.messages.impl.timeline.components.TimelineItemVirtualRow -import io.element.android.features.messages.impl.timeline.components.group.GroupHeaderView +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.messages.impl.timeline.components.TimelineItemRow import io.element.android.features.messages.impl.timeline.components.virtual.TimelineItemRoomBeginningView import io.element.android.features.messages.impl.timeline.components.virtual.TimelineLoadingMoreIndicator import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.di.aFakeTimelineItemPresenterFactories +import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentProvider -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent -import io.element.android.features.messages.impl.timeline.model.event.canBeRepliedTo -import io.element.android.features.messages.impl.timeline.session.SessionState import io.element.android.libraries.designsystem.animation.alphaAnimation import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.FloatingActionButton import io.element.android.libraries.designsystem.theme.components.Icon -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.theme.ElementTheme +import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.launch @Composable @@ -127,6 +118,7 @@ fun TimelineView( ) { timelineItem -> TimelineItemRow( timelineItem = timelineItem, + timelineRoomInfo = state.timelineRoomInfo, showReadReceipts = state.showReadReceipts, isLastOutgoingMessage = (timelineItem as? TimelineItem.Event)?.isMine == true && state.timelineItems.first().identifier() == timelineItem.identifier(), @@ -165,141 +157,44 @@ fun TimelineView( TimelineScrollHelper( isTimelineEmpty = state.timelineItems.isEmpty(), lazyListState = lazyListState, - hasNewItems = state.hasNewItems, + newEventState = state.newEventState, onScrollFinishedAt = ::onScrollFinishedAt ) } } -@Composable -private fun TimelineItemRow( - timelineItem: TimelineItem, - showReadReceipts: Boolean, - isLastOutgoingMessage: Boolean, - highlightedItem: String?, - userHasPermissionToSendMessage: Boolean, - sessionState: SessionState, - onUserDataClick: (UserId) -> Unit, - onClick: (TimelineItem.Event) -> Unit, - onLongClick: (TimelineItem.Event) -> Unit, - inReplyToClick: (EventId) -> Unit, - onReactionClick: (key: String, TimelineItem.Event) -> Unit, - onReactionLongClick: (key: String, TimelineItem.Event) -> Unit, - onMoreReactionsClick: (TimelineItem.Event) -> Unit, - onReadReceiptClick: (TimelineItem.Event) -> Unit, - onTimestampClicked: (TimelineItem.Event) -> Unit, - onSwipeToReply: (TimelineItem.Event) -> Unit, - eventSink: (TimelineEvents) -> Unit, - modifier: Modifier = Modifier -) { - when (timelineItem) { - is TimelineItem.Virtual -> { - TimelineItemVirtualRow( - virtual = timelineItem, - sessionState = sessionState, - modifier = modifier, - ) - } - is TimelineItem.Event -> { - if (timelineItem.content is TimelineItemStateContent) { - TimelineItemStateEventRow( - event = timelineItem, - isHighlighted = highlightedItem == timelineItem.identifier(), - onClick = { onClick(timelineItem) }, - onLongClick = { onLongClick(timelineItem) }, - eventSink = eventSink, - modifier = modifier, - ) - } else { - TimelineItemEventRow( - event = timelineItem, - showReadReceipts = showReadReceipts, - isLastOutgoingMessage = isLastOutgoingMessage, - isHighlighted = highlightedItem == timelineItem.identifier(), - canReply = userHasPermissionToSendMessage && timelineItem.content.canBeRepliedTo(), - onClick = { onClick(timelineItem) }, - onLongClick = { onLongClick(timelineItem) }, - onUserDataClick = onUserDataClick, - inReplyToClick = inReplyToClick, - onReactionClick = onReactionClick, - onReactionLongClick = onReactionLongClick, - onMoreReactionsClick = onMoreReactionsClick, - onReadReceiptClick = onReadReceiptClick, - onTimestampClicked = onTimestampClicked, - onSwipeToReply = { onSwipeToReply(timelineItem) }, - eventSink = eventSink, - modifier = modifier, - ) - } - } - is TimelineItem.GroupedEvents -> { - val isExpanded = rememberSaveable(key = timelineItem.identifier()) { mutableStateOf(false) } - - fun onExpandGroupClick() { - isExpanded.value = !isExpanded.value - } - - Column(modifier = modifier.animateContentSize()) { - GroupHeaderView( - text = pluralStringResource( - id = R.plurals.room_timeline_state_changes, - count = timelineItem.events.size, - timelineItem.events.size - ), - isExpanded = isExpanded.value, - isHighlighted = !isExpanded.value && timelineItem.events.any { it.identifier() == highlightedItem }, - onClick = ::onExpandGroupClick, - ) - if (isExpanded.value) { - Column { - timelineItem.events.forEach { subGroupEvent -> - TimelineItemRow( - timelineItem = subGroupEvent, - showReadReceipts = showReadReceipts, - isLastOutgoingMessage = isLastOutgoingMessage, - highlightedItem = highlightedItem, - sessionState = sessionState, - userHasPermissionToSendMessage = false, - onClick = onClick, - onLongClick = onLongClick, - inReplyToClick = inReplyToClick, - onUserDataClick = onUserDataClick, - onTimestampClicked = onTimestampClicked, - onReactionClick = onReactionClick, - onReactionLongClick = onReactionLongClick, - onMoreReactionsClick = onMoreReactionsClick, - onReadReceiptClick = onReadReceiptClick, - eventSink = eventSink, - onSwipeToReply = {}, - ) - } - } - } - } - } - } -} - @Composable private fun BoxScope.TimelineScrollHelper( isTimelineEmpty: Boolean, lazyListState: LazyListState, - hasNewItems: Boolean, + newEventState: NewEventState, onScrollFinishedAt: (Int) -> Unit, ) { val coroutineScope = rememberCoroutineScope() val isScrollFinished by remember { derivedStateOf { !lazyListState.isScrollInProgress } } - val canAutoScroll by remember { derivedStateOf { lazyListState.firstVisibleItemIndex < 3 } } + val canAutoScroll by remember { + derivedStateOf { + lazyListState.firstVisibleItemIndex < 3 + } + } - LaunchedEffect(canAutoScroll, hasNewItems) { - val shouldAutoScroll = isScrollFinished && canAutoScroll && hasNewItems - if (shouldAutoScroll) { - coroutineScope.launch { + fun scrollToBottom() { + coroutineScope.launch { + if (lazyListState.firstVisibleItemIndex > 10) { + lazyListState.scrollToItem(0) + } else { lazyListState.animateScrollToItem(0) } } } + LaunchedEffect(canAutoScroll, newEventState) { + val shouldAutoScroll = isScrollFinished && (canAutoScroll || newEventState == NewEventState.FromMe) + if (shouldAutoScroll) { + scrollToBottom() + } + } + LaunchedEffect(isScrollFinished, isTimelineEmpty) { if (isScrollFinished && !isTimelineEmpty) { // Notify the parent composable about the first visible item index when scrolling finishes @@ -313,15 +208,7 @@ private fun BoxScope.TimelineScrollHelper( modifier = Modifier .align(Alignment.BottomEnd) .padding(end = 24.dp, bottom = 12.dp), - onClick = { - coroutineScope.launch { - if (lazyListState.firstVisibleItemIndex > 10) { - lazyListState.scrollToItem(0) - } else { - lazyListState.animateScrollToItem(0) - } - } - } + onClick = ::scrollToBottom, ) } @@ -349,8 +236,8 @@ private fun JumpToBottomButton( modifier = Modifier .size(24.dp) .rotate(90f), - resourceId = CommonDrawables.ic_compound_arrow_right, - contentDescription = "", + imageVector = CompoundIcons.ArrowRight, + contentDescription = stringResource(id = CommonStrings.a11y_jump_to_bottom) ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ATimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ATimelineItemEventRow.kt new file mode 100644 index 0000000000..a2a0ae5f4e --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ATimelineItemEventRow.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components + +import androidx.compose.runtime.Composable +import io.element.android.features.messages.impl.timeline.TimelineRoomInfo +import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo +import io.element.android.features.messages.impl.timeline.model.TimelineItem + +// For previews +@Composable +internal fun ATimelineItemEventRow( + event: TimelineItem.Event, + timelineRoomInfo: TimelineRoomInfo = aTimelineRoomInfo(), + showReadReceipts: Boolean = false, + isLastOutgoingMessage: Boolean = false, + isHighlighted: Boolean = false, +) = TimelineItemEventRow( + event = event, + timelineRoomInfo = timelineRoomInfo, + showReadReceipts = showReadReceipts, + isLastOutgoingMessage = isLastOutgoingMessage, + isHighlighted = isHighlighted, + canReply = true, + onClick = {}, + onLongClick = {}, + onUserDataClick = {}, + inReplyToClick = {}, + onReactionClick = { _, _ -> }, + onReactionLongClick = { _, _ -> }, + onMoreReactionsClick = {}, + onReadReceiptClick = {}, + onSwipeToReply = {}, + onTimestampClicked = {}, + eventSink = {}, +) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt index bcb0efc96e..14bd831efc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt @@ -35,20 +35,20 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Shape import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState import io.element.android.features.messages.impl.timeline.model.bubble.BubbleStateProvider import io.element.android.libraries.core.extensions.to01 -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.messageFromMeBackground import io.element.android.libraries.designsystem.theme.messageFromOtherBackground -import io.element.android.libraries.theme.ElementTheme private val BUBBLE_RADIUS = 12.dp -private val BUBBLE_INCOMING_OFFSET = 16.dp +internal val BUBBLE_INCOMING_OFFSET = 16.dp // Design says: The maximum width of a bubble is still 3/4 of the screen width. But try with 85% now. private const val BUBBLE_WIDTH_RATIO = 0.85f @@ -58,9 +58,9 @@ private const val BUBBLE_WIDTH_RATIO = 0.85f fun MessageEventBubble( state: BubbleState, interactionSource: MutableInteractionSource, + onClick: () -> Unit, + onLongClick: () -> Unit, modifier: Modifier = Modifier, - onClick: () -> Unit = {}, - onLongClick: () -> Unit = {}, content: @Composable () -> Unit = {}, ) { fun bubbleShape(): Shape { @@ -91,18 +91,17 @@ fun MessageEventBubble( } fun Modifier.offsetForItem(): Modifier { - return if (state.isMine) { - this - } else { - offset(x = BUBBLE_INCOMING_OFFSET) + return when { + state.isMine -> this + state.timelineRoomInfo.isDirect -> this + else -> offset(x = BUBBLE_INCOMING_OFFSET) } } // Ignore state.isHighlighted for now, we need a design decision on it. - val backgroundBubbleColor = if (state.isMine) { - ElementTheme.colors.messageFromMeBackground - } else { - ElementTheme.colors.messageFromOtherBackground + val backgroundBubbleColor = when { + state.isMine -> ElementTheme.colors.messageFromMeBackground + else -> ElementTheme.colors.messageFromOtherBackground } val bubbleShape = bubbleShape() Box( @@ -144,6 +143,8 @@ internal fun MessageEventBubblePreview(@PreviewParameter(BubbleStateProvider::cl MessageEventBubble( state = state, interactionSource = remember { MutableInteractionSource() }, + onClick = {}, + onLongClick = {}, ) { // Render the state as a text to better understand the previews Box( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt index e4dfde37f9..6391c91c24 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt @@ -31,8 +31,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Surface private val CORNER_RADIUS = 8.dp @@ -42,9 +42,9 @@ private val CORNER_RADIUS = 8.dp fun MessageStateEventContainer( @Suppress("UNUSED_PARAMETER") isHighlighted: Boolean, interactionSource: MutableInteractionSource, + onClick: () -> Unit, + onLongClick: () -> Unit, modifier: Modifier = Modifier, - onClick: () -> Unit = {}, - onLongClick: () -> Unit = {}, content: @Composable () -> Unit = {}, ) { // Ignore isHighlighted for now, we need a design decision on it. @@ -73,12 +73,16 @@ internal fun MessageStateEventContainerPreview() = ElementPreview { MessageStateEventContainer( isHighlighted = false, interactionSource = remember { MutableInteractionSource() }, + onClick = {}, + onLongClick = {}, ) { Spacer(modifier = Modifier.size(width = 120.dp, height = 32.dp)) } MessageStateEventContainer( isHighlighted = true, interactionSource = remember { MutableInteractionSource() }, + onClick = {}, + onLongClick = {}, ) { Spacer(modifier = Modifier.size(width = 120.dp, height = 32.dp)) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt index a5220e1d90..d80bde4cf4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt @@ -52,7 +52,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable @OptIn(ExperimentalFoundationApi::class) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt index 5bc7efc852..3451c1590c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt @@ -33,16 +33,15 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.timeline.model.TimelineItem -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent -import io.element.android.libraries.core.bool.orFalse -import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.features.messages.impl.timeline.model.event.isEdited import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @OptIn(ExperimentalFoundationApi::class) @@ -55,7 +54,7 @@ fun TimelineEventTimestampView( ) { val formattedTime = event.sentTime val hasMessageSendingFailed = event.localSendState is LocalEventSendState.SendingFailed - val isMessageEdited = (event.content as? TimelineItemTextBasedContent)?.isEdited.orFalse() + val isMessageEdited = event.content.isEdited() val tint = if (hasMessageSendingFailed) MaterialTheme.colorScheme.error else null val clickModifier = if (hasMessageSendingFailed) { Modifier.combinedClickable( @@ -90,8 +89,8 @@ fun TimelineEventTimestampView( if (hasMessageSendingFailed && tint != null) { Spacer(modifier = Modifier.width(2.dp)) Icon( - resourceId = CommonDrawables.ic_compound_error, - contentDescription = "Error sending message", + imageVector = CompoundIcons.Error, + contentDescription = stringResource(id = CommonStrings.common_sending_failed), tint = tint, modifier = Modifier.size(15.dp, 18.dp), ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt index 5f7fff64ae..d22cfedae7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt @@ -17,6 +17,7 @@ package io.element.android.features.messages.impl.timeline.components import android.annotation.SuppressLint +import android.net.Uri import androidx.compose.foundation.Canvas import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -48,74 +49,66 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.platform.ViewConfiguration import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import androidx.constraintlayout.compose.ConstrainScope import androidx.constraintlayout.compose.ConstraintLayout +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.timeline.TimelineEvents +import io.element.android.features.messages.impl.timeline.TimelineRoomInfo import io.element.android.features.messages.impl.timeline.aTimelineItemEvent -import io.element.android.features.messages.impl.timeline.aTimelineItemReactions import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView import io.element.android.features.messages.impl.timeline.components.event.toExtraPadding import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewState import io.element.android.features.messages.impl.timeline.components.receipt.TimelineItemReadReceiptView +import io.element.android.features.messages.impl.timeline.model.InReplyToDetails +import io.element.android.features.messages.impl.timeline.model.InReplyToMetadata import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent -import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent +import io.element.android.features.messages.impl.timeline.model.metadata +import io.element.android.libraries.androidutils.system.openUrlInExternalApp import io.element.android.libraries.designsystem.colors.AvatarColorsProvider import io.element.android.libraries.designsystem.components.EqualWidthColumn import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.swipe.SwipeableActionsState import io.element.android.libraries.designsystem.swipe.rememberSwipeableActionsState import io.element.android.libraries.designsystem.text.toPx import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType -import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType -import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType -import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo -import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType -import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent -import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType -import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType -import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType +import io.element.android.libraries.matrix.api.permalink.PermalinkData +import io.element.android.libraries.matrix.api.permalink.PermalinkParser +import io.element.android.libraries.matrix.api.room.Mention import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail -import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo -import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.launch -import org.jsoup.Jsoup import kotlin.math.abs import kotlin.math.roundToInt @Composable fun TimelineItemEventRow( event: TimelineItem.Event, + timelineRoomInfo: TimelineRoomInfo, showReadReceipts: Boolean, isLastOutgoingMessage: Boolean, isHighlighted: Boolean, @@ -141,10 +134,17 @@ fun TimelineItemEventRow( } fun inReplyToClicked() { - val inReplyToEventId = (event.inReplyTo as? InReplyTo.Ready)?.eventId ?: return + val inReplyToEventId = event.inReplyTo?.eventId ?: return inReplyToClick(inReplyToEventId) } + fun onMentionClicked(mention: Mention) { + when (mention) { + is Mention.User -> onUserDataClick(mention.userId) + else -> Unit // TODO implement actions for other mentions being clicked + } + } + Column(modifier = modifier.fillMaxWidth()) { if (event.groupPosition.isNew()) { Spacer(modifier = Modifier.height(16.dp)) @@ -178,9 +178,8 @@ fun TimelineItemEventRow( state = state.draggableState, ), event = event, - showReadReceipts = showReadReceipts, - isLastOutgoingMessage = isLastOutgoingMessage, isHighlighted = isHighlighted, + timelineRoomInfo = timelineRoomInfo, interactionSource = interactionSource, onClick = onClick, onLongClick = onLongClick, @@ -190,7 +189,7 @@ fun TimelineItemEventRow( onReactionClicked = { emoji -> onReactionClick(emoji, event) }, onReactionLongClicked = { emoji -> onReactionLongClick(emoji, event) }, onMoreReactionsClicked = { onMoreReactionsClick(event) }, - onReadReceiptsClicked = { onReadReceiptClick(event) }, + onMentionClicked = ::onMentionClicked, eventSink = eventSink, ) } @@ -198,9 +197,8 @@ fun TimelineItemEventRow( } else { TimelineItemEventRowContent( event = event, - showReadReceipts = showReadReceipts, - isLastOutgoingMessage = isLastOutgoingMessage, isHighlighted = isHighlighted, + timelineRoomInfo = timelineRoomInfo, interactionSource = interactionSource, onClick = onClick, onLongClick = onLongClick, @@ -210,10 +208,21 @@ fun TimelineItemEventRow( onReactionClicked = { emoji -> onReactionClick(emoji, event) }, onReactionLongClicked = { emoji -> onReactionLongClick(emoji, event) }, onMoreReactionsClicked = { onMoreReactionsClick(event) }, - onReadReceiptsClicked = { onReadReceiptClick(event) }, + onMentionClicked = ::onMentionClicked, eventSink = eventSink, ) } + // Read receipts / Send state + TimelineItemReadReceiptView( + state = ReadReceiptViewState( + sendState = event.localSendState, + isLastOutgoingMessage = isLastOutgoingMessage, + receipts = event.readReceiptState.receipts, + ), + showReadReceipts = showReadReceipts, + onReadReceiptsClicked = { onReadReceiptClick(event) }, + modifier = Modifier.padding(top = 4.dp), + ) } } @@ -243,9 +252,8 @@ private fun SwipeSensitivity( @Composable private fun TimelineItemEventRowContent( event: TimelineItem.Event, - showReadReceipts: Boolean, - isLastOutgoingMessage: Boolean, isHighlighted: Boolean, + timelineRoomInfo: TimelineRoomInfo, interactionSource: MutableInteractionSource, onClick: () -> Unit, onLongClick: () -> Unit, @@ -253,9 +261,9 @@ private fun TimelineItemEventRowContent( inReplyToClicked: () -> Unit, onUserDataClicked: () -> Unit, onReactionClicked: (emoji: String) -> Unit, - onReadReceiptsClicked: () -> Unit, onReactionLongClicked: (emoji: String) -> Unit, onMoreReactionsClicked: (event: TimelineItem.Event) -> Unit, + onMentionClicked: (Mention) -> Unit, eventSink: (TimelineEvents) -> Unit, modifier: Modifier = Modifier, ) { @@ -274,12 +282,11 @@ private fun TimelineItemEventRowContent( sender, message, reactions, - readReceipts, ) = createRefs() // Sender val avatarStrokeSize = 3.dp - if (event.showSenderInformation) { + if (event.showSenderInformation && !timelineRoomInfo.isDirect) { MessageSenderInformation( event.safeSenderName, event.senderAvatar, @@ -299,6 +306,7 @@ private fun TimelineItemEventRowContent( groupPosition = event.groupPosition, isMine = event.isMine, isHighlighted = isHighlighted, + timelineRoomInfo = timelineRoomInfo, ) MessageEventBubble( modifier = Modifier @@ -313,13 +321,12 @@ private fun TimelineItemEventRowContent( ) { MessageEventBubbleContent( event = event, - interactionSource = interactionSource, - onMessageClick = onClick, onMessageLongClick = onLongClick, inReplyToClick = inReplyToClicked, onTimestampClicked = { onTimestampClicked(event) }, + onMentionClicked = onMentionClicked, eventSink = eventSink, ) } @@ -338,28 +345,18 @@ private fun TimelineItemEventRowContent( linkStartOrEnd(event) } .zIndex(1f) - .padding(start = if (event.isMine) 16.dp else 36.dp, end = 16.dp) + .padding( + // Note: due to the applied constraints, start is left for other's message and right for mine + // In design we want a offset of 6.dp compare to the bubble, so start is 22.dp (16 + 6) + start = when { + event.isMine -> 22.dp + timelineRoomInfo.isDirect -> 22.dp + else -> 22.dp + BUBBLE_INCOMING_OFFSET + }, + end = 16.dp + ) ) } - - // Read receipts / Send state - TimelineItemReadReceiptView( - state = ReadReceiptViewState( - sendState = event.localSendState, - isLastOutgoingMessage = isLastOutgoingMessage, - receipts = event.readReceiptState.receipts, - ), - showReadReceipts = showReadReceipts, - onReadReceiptsClicked = onReadReceiptsClicked, - modifier = Modifier - .constrainAs(readReceipts) { - if (event.reactionsState.reactions.isNotEmpty()) { - top.linkTo(reactions.bottom, margin = 4.dp) - } else { - top.linkTo(message.bottom, margin = 4.dp) - } - } - ) } } @@ -393,6 +390,7 @@ private fun MessageSenderInformation( Avatar(senderAvatar) Spacer(modifier = Modifier.width(4.dp)) Text( + modifier = Modifier.clipToBounds(), text = sender, maxLines = 1, overflow = TextOverflow.Ellipsis, @@ -406,11 +404,10 @@ private fun MessageSenderInformation( @Composable private fun MessageEventBubbleContent( event: TimelineItem.Event, - interactionSource: MutableInteractionSource, - onMessageClick: () -> Unit, onMessageLongClick: () -> Unit, inReplyToClick: () -> Unit, onTimestampClicked: () -> Unit, + onMentionClicked: (Mention) -> Unit, eventSink: (TimelineEvents) -> Unit, @SuppressLint("ModifierParameter") @Suppress("ModifierNaming") @@ -431,7 +428,8 @@ private fun MessageEventBubbleContent( verticalAlignment = Alignment.CenterVertically, ) { Icon( - resourceId = CommonDrawables.ic_thread_decoration, + modifier = Modifier.height(14.dp), + imageVector = CompoundIcons.Threads, contentDescription = null, tint = ElementTheme.colors.iconSecondary, ) @@ -451,7 +449,7 @@ private fun MessageEventBubbleContent( ) { when (timestampPosition) { TimestampPosition.Overlay -> - Box(modifier) { + Box(modifier, contentAlignment = Alignment.Center) { content() TimelineEventTimestampView( event = event, @@ -496,9 +494,10 @@ private fun MessageEventBubbleContent( fun CommonLayout( timestampPosition: TimestampPosition, showThreadDecoration: Boolean, - inReplyToDetails: InReplyTo.Ready?, + inReplyToDetails: InReplyToDetails?, modifier: Modifier = Modifier ) { + val context = LocalContext.current val timestampLayoutModifier: Modifier val contentModifier: Modifier when { @@ -532,25 +531,32 @@ private fun MessageEventBubbleContent( ) { TimelineItemEventContentView( content = event.content, - isMine = event.isMine, - interactionSource = interactionSource, - onClick = onMessageClick, - onLongClick = onMessageLongClick, + onLinkClicked = { url -> + when (val permalink = PermalinkParser.parse(Uri.parse(url))) { + is PermalinkData.UserLink -> { + onMentionClicked(Mention.User(UserId(permalink.userId))) + } + is PermalinkData.RoomLink -> { + onMentionClicked(Mention.Room(permalink.getRoomId(), permalink.getRoomAlias())) + } + is PermalinkData.FallbackLink, + is PermalinkData.RoomEmailInviteLink -> { + context.openUrlInExternalApp(url) + } + } + }, extraPadding = event.toExtraPadding(), eventSink = eventSink, modifier = contentModifier, ) } } - val inReplyTo = @Composable { inReplyToReady: InReplyTo.Ready -> - val senderName = inReplyToReady.senderDisplayName ?: inReplyToReady.senderId.value - val attachmentThumbnailInfo = attachmentThumbnailInfoForInReplyTo(inReplyToReady) - val text = textForInReplyTo(inReplyToReady) + val inReplyTo = @Composable { inReplyTo: InReplyToDetails -> + val senderName = inReplyTo.senderDisplayName ?: inReplyTo.senderId.value val topPadding = if (showThreadDecoration) 0.dp else 8.dp ReplyToContent( senderName = senderName, - text = text, - attachmentThumbnailInfo = attachmentThumbnailInfo, + metadata = inReplyTo.metadata(), modifier = Modifier .padding(top = topPadding, start = 8.dp, end = 8.dp) .clip(RoundedCornerShape(6.dp)) @@ -580,11 +586,10 @@ private fun MessageEventBubbleContent( is TimelineItemPollContent -> TimestampPosition.Below else -> TimestampPosition.Default } - val replyToDetails = event.inReplyTo as? InReplyTo.Ready CommonLayout( showThreadDecoration = event.isThreaded, timestampPosition = timestampPosition, - inReplyToDetails = replyToDetails, + inReplyToDetails = event.inReplyTo, modifier = bubbleModifier ) } @@ -592,11 +597,10 @@ private fun MessageEventBubbleContent( @Composable private fun ReplyToContent( senderName: String, - text: String?, - attachmentThumbnailInfo: AttachmentThumbnailInfo?, + metadata: InReplyToMetadata?, modifier: Modifier = Modifier, ) { - val paddings = if (attachmentThumbnailInfo != null) { + val paddings = if (metadata is InReplyToMetadata.Thumbnail) { PaddingValues(start = 4.dp, end = 12.dp, top = 4.dp, bottom = 4.dp) } else { PaddingValues(horizontal = 12.dp, vertical = 4.dp) @@ -606,9 +610,9 @@ private fun ReplyToContent( .background(MaterialTheme.colorScheme.surface) .padding(paddings) ) { - if (attachmentThumbnailInfo != null) { + if (metadata is InReplyToMetadata.Thumbnail) { AttachmentThumbnail( - info = attachmentThumbnailInfo, + info = metadata.attachmentThumbnailInfo, backgroundColor = MaterialTheme.colorScheme.surfaceVariant, modifier = Modifier .size(36.dp) @@ -626,7 +630,7 @@ private fun ReplyToContent( overflow = TextOverflow.Ellipsis, ) Text( - text = text.orEmpty(), + text = metadata?.text.orEmpty(), style = ElementTheme.typography.fontBodyMdRegular, textAlign = TextAlign.Start, color = ElementTheme.materialColors.secondary, @@ -637,57 +641,12 @@ private fun ReplyToContent( } } -private fun attachmentThumbnailInfoForInReplyTo(inReplyTo: InReplyTo.Ready): AttachmentThumbnailInfo? { - val messageContent = inReplyTo.content as? MessageContent ?: return null - return when (val type = messageContent.type) { - is ImageMessageType -> AttachmentThumbnailInfo( - thumbnailSource = type.info?.thumbnailSource ?: type.source, - textContent = messageContent.body, - type = AttachmentThumbnailType.Image, - blurHash = type.info?.blurhash, - ) - is VideoMessageType -> AttachmentThumbnailInfo( - thumbnailSource = type.info?.thumbnailSource, - textContent = messageContent.body, - type = AttachmentThumbnailType.Video, - blurHash = type.info?.blurhash, - ) - is FileMessageType -> AttachmentThumbnailInfo( - thumbnailSource = type.info?.thumbnailSource, - textContent = messageContent.body, - type = AttachmentThumbnailType.File, - ) - is LocationMessageType -> AttachmentThumbnailInfo( - textContent = messageContent.body, - type = AttachmentThumbnailType.Location, - ) - is AudioMessageType -> AttachmentThumbnailInfo( - textContent = messageContent.body, - type = AttachmentThumbnailType.Audio, - ) - is VoiceMessageType -> AttachmentThumbnailInfo( - type = AttachmentThumbnailType.Voice, - ) - else -> null - } -} - -@Composable -private fun textForInReplyTo(inReplyTo: InReplyTo.Ready): String { - val messageContent = inReplyTo.content as? MessageContent ?: return "" - return when (messageContent.type) { - is LocationMessageType -> stringResource(CommonStrings.common_shared_location) - is VoiceMessageType -> stringResource(CommonStrings.common_voice_message) - else -> messageContent.body - } -} - @PreviewsDayNight @Composable internal fun TimelineItemEventRowPreview() = ElementPreview { Column { sequenceOf(false, true).forEach { - TimelineItemEventRow( + ATimelineItemEventRow( event = aTimelineItemEvent( isMine = it, content = aTimelineItemTextContent().copy( @@ -696,253 +655,16 @@ internal fun TimelineItemEventRowPreview() = ElementPreview { ), groupPosition = TimelineItemGroupPosition.First, ), - showReadReceipts = false, - isLastOutgoingMessage = false, - isHighlighted = false, - canReply = true, - onClick = {}, - onLongClick = {}, - onUserDataClick = {}, - inReplyToClick = {}, - onReactionClick = { _, _ -> }, - onReactionLongClick = { _, _ -> }, - onMoreReactionsClick = {}, - onReadReceiptClick = {}, - onTimestampClicked = {}, - onSwipeToReply = {}, - eventSink = {}, ) - TimelineItemEventRow( + ATimelineItemEventRow( event = aTimelineItemEvent( isMine = it, content = aTimelineItemImageContent().copy( - aspectRatio = 5f + aspectRatio = 2.5f ), groupPosition = TimelineItemGroupPosition.Last, ), - showReadReceipts = false, - isLastOutgoingMessage = false, - isHighlighted = false, - canReply = true, - onClick = {}, - onLongClick = {}, - onUserDataClick = {}, - inReplyToClick = {}, - onReactionClick = { _, _ -> }, - onReactionLongClick = { _, _ -> }, - onMoreReactionsClick = {}, - onReadReceiptClick = {}, - onTimestampClicked = {}, - onSwipeToReply = {}, - eventSink = {}, ) } } } - -@PreviewsDayNight -@Composable -internal fun TimelineItemEventRowWithReplyPreview() = ElementPreview { - Column { - sequenceOf(false, true).forEach { - val replyContent = if (it) { - // Short - "Message which are being replied." - } else { - // Long, to test 2 lines and ellipsis) - "Message which are being replied, and which was long enough to be displayed on two lines (only!)." - } - TimelineItemEventRow( - event = aTimelineItemEvent( - isMine = it, - content = aTimelineItemTextContent().copy( - body = "A long text which will be displayed on several lines and" + - " hopefully can be manually adjusted to test different behaviors." - ), - inReplyTo = aInReplyToReady(replyContent), - groupPosition = TimelineItemGroupPosition.First, - ), - showReadReceipts = false, - isLastOutgoingMessage = false, - isHighlighted = false, - canReply = true, - onClick = {}, - onLongClick = {}, - onUserDataClick = {}, - inReplyToClick = {}, - onReactionClick = { _, _ -> }, - onReactionLongClick = { _, _ -> }, - onMoreReactionsClick = {}, - onReadReceiptClick = {}, - onTimestampClicked = {}, - onSwipeToReply = {}, - eventSink = {}, - ) - TimelineItemEventRow( - event = aTimelineItemEvent( - isMine = it, - content = aTimelineItemImageContent().copy( - aspectRatio = 5f - ), - inReplyTo = aInReplyToReady(replyContent), - isThreaded = true, - groupPosition = TimelineItemGroupPosition.Last, - ), - showReadReceipts = false, - isLastOutgoingMessage = false, - isHighlighted = false, - canReply = true, - onClick = {}, - onLongClick = {}, - onUserDataClick = {}, - inReplyToClick = {}, - onReactionClick = { _, _ -> }, - onReactionLongClick = { _, _ -> }, - onMoreReactionsClick = {}, - onReadReceiptClick = {}, - onTimestampClicked = {}, - onSwipeToReply = {}, - eventSink = {}, - ) - } - } -} - -private fun aInReplyToReady( - replyContent: String, -): InReplyTo.Ready { - return InReplyTo.Ready( - eventId = EventId("\$event"), - content = MessageContent(replyContent, null, false, false, TextMessageType(replyContent, null)), - senderId = UserId("@Sender:domain"), - senderDisplayName = "Sender", - senderAvatarUrl = null, - ) -} - -@PreviewsDayNight -@Composable -internal fun TimelineItemEventRowTimestampPreview( - @PreviewParameter(TimelineItemEventForTimestampViewProvider::class) event: TimelineItem.Event -) = ElementPreview { - Column { - val oldContent = event.content as TimelineItemTextContent - listOf( - "Text", - "Text longer, displayed on 1 line", - "Text which should be rendered on several lines", - ).forEach { str -> - listOf(false, true).forEach { useDocument -> - TimelineItemEventRow( - event = event.copy( - content = oldContent.copy( - body = str, - htmlDocument = if (useDocument) Jsoup.parse(str) else null, - ), - reactionsState = aTimelineItemReactions(count = 0), - senderDisplayName = if (useDocument) "Document case" else "Text case", - ), - showReadReceipts = false, - isLastOutgoingMessage = false, - isHighlighted = false, - canReply = true, - onClick = {}, - onLongClick = {}, - onUserDataClick = {}, - inReplyToClick = {}, - onReactionClick = { _, _ -> }, - onReactionLongClick = { _, _ -> }, - onMoreReactionsClick = {}, - onReadReceiptClick = {}, - onTimestampClicked = {}, - onSwipeToReply = {}, - eventSink = {}, - ) - } - } - } -} - -@PreviewsDayNight -@Composable -internal fun TimelineItemEventRowWithManyReactionsPreview() = ElementPreview { - Column { - listOf(false, true).forEach { isMine -> - TimelineItemEventRow( - event = aTimelineItemEvent( - isMine = isMine, - content = aTimelineItemTextContent().copy( - body = "A couple of multi-line messages with many reactions attached." + - " One sent by me and another from someone else." - ), - timelineItemReactions = aTimelineItemReactions(count = 20), - ), - showReadReceipts = false, - isLastOutgoingMessage = false, - isHighlighted = false, - canReply = true, - onClick = {}, - onLongClick = {}, - onUserDataClick = {}, - inReplyToClick = {}, - onReactionClick = { _, _ -> }, - onReactionLongClick = { _, _ -> }, - onMoreReactionsClick = {}, - onReadReceiptClick = {}, - onSwipeToReply = {}, - onTimestampClicked = {}, - eventSink = {}, - ) - } - } -} - -// Note: no need for light/dark variant for this preview -@Preview -@Composable -internal fun TimelineItemEventRowLongSenderNamePreview() = ElementPreviewLight { - TimelineItemEventRow( - event = aTimelineItemEvent( - senderDisplayName = "a long sender display name to test single line and ellipsis at the end of the line", - ), - showReadReceipts = false, - isLastOutgoingMessage = false, - isHighlighted = false, - canReply = true, - onClick = {}, - onLongClick = {}, - onUserDataClick = {}, - inReplyToClick = {}, - onReactionClick = { _, _ -> }, - onReactionLongClick = { _, _ -> }, - onMoreReactionsClick = {}, - onReadReceiptClick = {}, - onSwipeToReply = {}, - onTimestampClicked = {}, - eventSink = {}, - ) -} - -// Note: no need for light/dark variant for this preview, we only look at the timestamp position -@Preview -@Composable -internal fun TimelineItemEventTimestampBelowPreview() = ElementPreviewLight { - TimelineItemEventRow( - event = aTimelineItemEvent(content = aTimelineItemPollContent()), - showReadReceipts = false, - isLastOutgoingMessage = false, - isHighlighted = false, - canReply = true, - onClick = {}, - onLongClick = {}, - onUserDataClick = {}, - inReplyToClick = {}, - onReactionClick = { _, _ -> }, - onReactionLongClick = { _, _ -> }, - onMoreReactionsClick = {}, - onReadReceiptClick = {}, - onSwipeToReply = {}, - onTimestampClicked = {}, - eventSink = {}, - ) -} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowForDirectRoomPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowForDirectRoomPreview.kt new file mode 100644 index 0000000000..4443c00f3d --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowForDirectRoomPreview.kt @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import io.element.android.features.messages.impl.timeline.aTimelineItemEvent +import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo +import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight + +@PreviewsDayNight +@Composable +internal fun TimelineItemEventRowForDirectRoomPreview() = ElementPreview { + Column { + sequenceOf(false, true).forEach { + ATimelineItemEventRow( + event = aTimelineItemEvent( + isMine = it, + content = aTimelineItemTextContent().copy( + body = "A long text which will be displayed on several lines and" + + " hopefully can be manually adjusted to test different behaviors." + ), + groupPosition = TimelineItemGroupPosition.First, + ), + timelineRoomInfo = aTimelineRoomInfo( + isDirect = true, + ), + ) + ATimelineItemEventRow( + event = aTimelineItemEvent( + isMine = it, + content = aTimelineItemImageContent().copy( + aspectRatio = 5f + ), + groupPosition = TimelineItemGroupPosition.Last, + ), + timelineRoomInfo = aTimelineRoomInfo( + isDirect = true, + ), + ) + } + } +} + diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowLongSenderNamePreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowLongSenderNamePreview.kt new file mode 100644 index 0000000000..061ad75690 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowLongSenderNamePreview.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import io.element.android.features.messages.impl.timeline.aTimelineItemEvent +import io.element.android.libraries.designsystem.preview.ElementPreviewLight + +// Note: no need for light/dark variant for this preview +@Preview +@Composable +internal fun TimelineItemEventRowLongSenderNamePreview() = ElementPreviewLight { + ATimelineItemEventRow( + event = aTimelineItemEvent( + senderDisplayName = "a long sender display name to test single line and ellipsis at the end of the line", + ), + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowTimestampPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowTimestampPreview.kt new file mode 100644 index 0000000000..a1bef47731 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowTimestampPreview.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.PreviewParameter +import io.element.android.features.messages.impl.timeline.aTimelineItemReactions +import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import org.jsoup.Jsoup + +@PreviewsDayNight +@Composable +internal fun TimelineItemEventRowTimestampPreview( + @PreviewParameter(TimelineItemEventForTimestampViewProvider::class) event: TimelineItem.Event +) = ElementPreview { + Column { + val oldContent = event.content as TimelineItemTextContent + listOf( + "Text", + "Text longer, displayed on 1 line", + "Text which should be rendered on several lines", + ).forEach { str -> + listOf(false, true).forEach { useDocument -> + ATimelineItemEventRow( + event = event.copy( + content = oldContent.copy( + body = str, + htmlDocument = if (useDocument) Jsoup.parse(str) else null, + ), + reactionsState = aTimelineItemReactions(count = 0), + senderDisplayName = if (useDocument) "Document case" else "Text case", + ), + ) + } + } + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithManyReactionsPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithManyReactionsPreview.kt new file mode 100644 index 0000000000..bbd65a62ad --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithManyReactionsPreview.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import io.element.android.features.messages.impl.timeline.aTimelineItemEvent +import io.element.android.features.messages.impl.timeline.aTimelineItemReactions +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight + +@PreviewsDayNight +@Composable +internal fun TimelineItemEventRowWithManyReactionsPreview() = ElementPreview { + Column { + listOf(false, true).forEach { isMine -> + ATimelineItemEventRow( + event = aTimelineItemEvent( + isMine = isMine, + content = aTimelineItemTextContent().copy( + body = "A couple of multi-line messages with many reactions attached." + + " One sent by me and another from someone else." + ), + timelineItemReactions = aTimelineItemReactions(count = 20), + ), + ) + } + } +} + diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithRRPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithRRPreview.kt new file mode 100644 index 0000000000..4a9972d19b --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithRRPreview.kt @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.PreviewParameter +import io.element.android.features.messages.impl.timeline.aTimelineItemEvent +import io.element.android.features.messages.impl.timeline.aTimelineItemReactions +import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewState +import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewStateForTimelineItemEventRowProvider +import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight + +// Note: I add to reduce the size of the fun name, or it does not compile. +// Previous name: TimelineItemEventRowWithSendingStateAndReadReceiptPreview +@PreviewsDayNight +@Composable +internal fun TimelineItemEventRowWithRRPreview( + @PreviewParameter(ReadReceiptViewStateForTimelineItemEventRowProvider::class) state: ReadReceiptViewState, +) = ElementPreview { + Column { + // A message from someone else + ATimelineItemEventRow( + event = aTimelineItemEvent( + isMine = false, + sendState = null, + content = aTimelineItemTextContent().copy( + body = "A message from someone else" + ), + timelineItemReactions = aTimelineItemReactions(count = 0), + readReceiptState = TimelineItemReadReceipts(state.receipts), + ), + showReadReceipts = true, + isLastOutgoingMessage = false, + ) + // A message from current user + ATimelineItemEventRow( + event = aTimelineItemEvent( + isMine = true, + sendState = state.sendState, + content = aTimelineItemTextContent().copy( + body = "A message from me" + ), + timelineItemReactions = aTimelineItemReactions(count = 0), + readReceiptState = TimelineItemReadReceipts(state.receipts), + ), + showReadReceipts = true, + isLastOutgoingMessage = false, + ) + // Another message from current user + ATimelineItemEventRow( + event = aTimelineItemEvent( + isMine = true, + sendState = state.sendState, + content = aTimelineItemTextContent().copy( + body = "A last message from me" + ), + timelineItemReactions = aTimelineItemReactions(count = 0), + readReceiptState = TimelineItemReadReceipts(state.receipts), + ), + showReadReceipts = true, + isLastOutgoingMessage = true, + ) + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt new file mode 100644 index 0000000000..ed046f1735 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.messages.impl.timeline.aTimelineItemEvent +import io.element.android.features.messages.impl.timeline.aTimelineItemReactions +import io.element.android.features.messages.impl.timeline.model.InReplyToDetails +import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrix.api.poll.PollKind +import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.EventContent +import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.MessageType +import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentMapOf + +@PreviewsDayNight +@Composable +internal fun TimelineItemEventRowWithReplyPreview( + @PreviewParameter(InReplyToDetailsProvider::class) inReplyToDetails: InReplyToDetails, +) = ElementPreview { + Column { + sequenceOf(false, true).forEach { + ATimelineItemEventRow( + event = aTimelineItemEvent( + isMine = it, + timelineItemReactions = aTimelineItemReactions(count = 0), + content = aTimelineItemTextContent().copy( + body = "A reply." + ), + inReplyTo = inReplyToDetails, + groupPosition = TimelineItemGroupPosition.First, + ), + ) + ATimelineItemEventRow( + event = aTimelineItemEvent( + isMine = it, + timelineItemReactions = aTimelineItemReactions(count = 0), + content = aTimelineItemImageContent().copy( + aspectRatio = 2.5f + ), + inReplyTo = inReplyToDetails, + isThreaded = true, + groupPosition = TimelineItemGroupPosition.Last, + ), + ) + } + } +} + +class InReplyToDetailsProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aMessageContent( + body = "Message which are being replied.", + type = TextMessageType("Message which are being replied.", null) + ), + aMessageContent( + body = "Message which are being replied, and which was long enough to be displayed on two lines (only!).", + type = TextMessageType("Message which are being replied, and which was long enough to be displayed on two lines (only!).", null) + ), + aMessageContent( + body = "Video", + type = VideoMessageType("Video", MediaSource("url"), null), + ), + aMessageContent( + body = "Audio", + type = AudioMessageType("Audio", MediaSource("url"), null), + ), + aMessageContent( + body = "Voice", + type = VoiceMessageType("Voice", MediaSource("url"), null, null), + ), + aMessageContent( + body = "Image", + type = ImageMessageType("Image", MediaSource("url"), null), + ), + aMessageContent( + body = "File", + type = FileMessageType("File", MediaSource("url"), null), + ), + aMessageContent( + body = "Location", + type = LocationMessageType("Location", "geo:1,2", null), + ), + aMessageContent( + body = "Notice", + type = NoticeMessageType("Notice", null), + ), + aMessageContent( + body = "Emote", + type = EmoteMessageType("Emote", null), + ), + PollContent( + question = "Poll which are being replied.", + kind = PollKind.Disclosed, + maxSelections = 1u, + answers = persistentListOf(), + votes = persistentMapOf(), + endTime = null, + isEdited = false, + ), + ).map { + aInReplyToDetails( + eventContent = it, + ) + } + + private fun aMessageContent( + body: String, + type: MessageType, + ) = MessageContent( + body = body, + inReplyTo = null, + isEdited = false, + isThreaded = false, + type = type, + ) + + private fun aInReplyToDetails( + eventContent: EventContent, + ) = InReplyToDetails( + eventId = EventId("\$event"), + eventContent = eventContent, + senderId = UserId("@Sender:domain"), + senderDisplayName = "Sender", + senderAvatarUrl = null, + textContent = (eventContent as? MessageContent)?.body.orEmpty(), + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventTimestampBelowPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventTimestampBelowPreview.kt new file mode 100644 index 0000000000..992ec63e7d --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventTimestampBelowPreview.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import io.element.android.features.messages.impl.timeline.aTimelineItemEvent +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent +import io.element.android.libraries.designsystem.preview.ElementPreviewLight + +// Note: no need for light/dark variant for this preview, we only look at the timestamp position +@Preview +@Composable +internal fun TimelineItemEventTimestampBelowPreview() = ElementPreviewLight { + ATimelineItemEventRow( + event = aTimelineItemEvent(content = aTimelineItemPollContent()), + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt new file mode 100644 index 0000000000..d52795c052 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components + +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.pluralStringResource +import io.element.android.features.messages.impl.R +import io.element.android.features.messages.impl.timeline.TimelineRoomInfo +import io.element.android.features.messages.impl.timeline.TimelineEvents +import io.element.android.features.messages.impl.timeline.aGroupedEvents +import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo +import io.element.android.features.messages.impl.timeline.components.group.GroupHeaderView +import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewState +import io.element.android.features.messages.impl.timeline.components.receipt.TimelineItemReadReceiptView +import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.session.SessionState +import io.element.android.features.messages.impl.timeline.session.aSessionState +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.UserId + +@Composable +fun TimelineItemGroupedEventsRow( + timelineItem: TimelineItem.GroupedEvents, + timelineRoomInfo: TimelineRoomInfo, + showReadReceipts: Boolean, + isLastOutgoingMessage: Boolean, + highlightedItem: String?, + sessionState: SessionState, + onClick: (TimelineItem.Event) -> Unit, + onLongClick: (TimelineItem.Event) -> Unit, + inReplyToClick: (EventId) -> Unit, + onUserDataClick: (UserId) -> Unit, + onTimestampClicked: (TimelineItem.Event) -> Unit, + onReactionClick: (key: String, TimelineItem.Event) -> Unit, + onReactionLongClick: (key: String, TimelineItem.Event) -> Unit, + onMoreReactionsClick: (TimelineItem.Event) -> Unit, + onReadReceiptClick: (TimelineItem.Event) -> Unit, + eventSink: (TimelineEvents) -> Unit, + modifier: Modifier = Modifier +) { + val isExpanded = rememberSaveable(key = timelineItem.identifier()) { mutableStateOf(false) } + + fun onExpandGroupClick() { + isExpanded.value = !isExpanded.value + } + + TimelineItemGroupedEventsRowContent( + isExpanded = isExpanded.value, + onExpandGroupClick = ::onExpandGroupClick, + timelineItem = timelineItem, + timelineRoomInfo = timelineRoomInfo, + highlightedItem = highlightedItem, + showReadReceipts = showReadReceipts, + isLastOutgoingMessage = isLastOutgoingMessage, + sessionState = sessionState, + onClick = onClick, + onLongClick = onLongClick, + inReplyToClick = inReplyToClick, + onUserDataClick = onUserDataClick, + onTimestampClicked = onTimestampClicked, + onReactionClick = onReactionClick, + onReactionLongClick = onReactionLongClick, + onMoreReactionsClick = onMoreReactionsClick, + onReadReceiptClick = onReadReceiptClick, + eventSink = eventSink, + modifier = modifier, + ) +} + +@Composable +private fun TimelineItemGroupedEventsRowContent( + isExpanded: Boolean, + onExpandGroupClick: () -> Unit, + timelineItem: TimelineItem.GroupedEvents, + timelineRoomInfo: TimelineRoomInfo, + highlightedItem: String?, + showReadReceipts: Boolean, + isLastOutgoingMessage: Boolean, + sessionState: SessionState, + onClick: (TimelineItem.Event) -> Unit, + onLongClick: (TimelineItem.Event) -> Unit, + inReplyToClick: (EventId) -> Unit, + onUserDataClick: (UserId) -> Unit, + onTimestampClicked: (TimelineItem.Event) -> Unit, + onReactionClick: (key: String, TimelineItem.Event) -> Unit, + onReactionLongClick: (key: String, TimelineItem.Event) -> Unit, + onMoreReactionsClick: (TimelineItem.Event) -> Unit, + onReadReceiptClick: (TimelineItem.Event) -> Unit, + eventSink: (TimelineEvents) -> Unit, + modifier: Modifier = Modifier, +) { + Column(modifier = modifier.animateContentSize()) { + GroupHeaderView( + text = pluralStringResource( + id = R.plurals.room_timeline_state_changes, + count = timelineItem.events.size, + timelineItem.events.size + ), + isExpanded = isExpanded, + isHighlighted = !isExpanded && timelineItem.events.any { it.identifier() == highlightedItem }, + onClick = onExpandGroupClick, + ) + if (isExpanded) { + Column { + timelineItem.events.forEach { subGroupEvent -> + TimelineItemRow( + timelineItem = subGroupEvent, + timelineRoomInfo = timelineRoomInfo, + showReadReceipts = showReadReceipts, + isLastOutgoingMessage = isLastOutgoingMessage, + highlightedItem = highlightedItem, + sessionState = sessionState, + userHasPermissionToSendMessage = false, + onClick = onClick, + onLongClick = onLongClick, + inReplyToClick = inReplyToClick, + onUserDataClick = onUserDataClick, + onTimestampClicked = onTimestampClicked, + onReactionClick = onReactionClick, + onReactionLongClick = onReactionLongClick, + onMoreReactionsClick = onMoreReactionsClick, + onReadReceiptClick = onReadReceiptClick, + eventSink = eventSink, + onSwipeToReply = {}, + ) + } + } + } else if (showReadReceipts) { + TimelineItemReadReceiptView( + state = ReadReceiptViewState( + sendState = null, + isLastOutgoingMessage = false, + receipts = timelineItem.aggregatedReadReceipts, + ), + showReadReceipts = true, + onReadReceiptsClicked = onExpandGroupClick + ) + } + } +} + +@PreviewsDayNight +@Composable +internal fun TimelineItemGroupedEventsRowContentExpandedPreview() = ElementPreview { + TimelineItemGroupedEventsRowContent( + isExpanded = true, + onExpandGroupClick = {}, + timelineItem = aGroupedEvents(), + timelineRoomInfo = aTimelineRoomInfo(), + highlightedItem = null, + showReadReceipts = true, + isLastOutgoingMessage = false, + sessionState = aSessionState(), + onClick = {}, + onLongClick = {}, + inReplyToClick = {}, + onUserDataClick = {}, + onTimestampClicked = {}, + onReactionClick = { _, _ -> }, + onReactionLongClick = { _, _ -> }, + onMoreReactionsClick = {}, + onReadReceiptClick = {}, + eventSink = {}, + ) +} + +@PreviewsDayNight +@Composable +internal fun TimelineItemGroupedEventsRowContentCollapsePreview() = ElementPreview { + TimelineItemGroupedEventsRowContent( + isExpanded = false, + onExpandGroupClick = {}, + timelineItem = aGroupedEvents(), + timelineRoomInfo = aTimelineRoomInfo(), + highlightedItem = null, + showReadReceipts = true, + isLastOutgoingMessage = false, + sessionState = aSessionState(), + onClick = {}, + onLongClick = {}, + inReplyToClick = {}, + onUserDataClick = {}, + onTimestampClicked = {}, + onReactionClick = { _, _ -> }, + onReactionLongClick = { _, _ -> }, + onMoreReactionsClick = {}, + onReadReceiptClick = {}, + eventSink = {}, + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt new file mode 100644 index 0000000000..92dbbe46b6 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import io.element.android.features.messages.impl.timeline.TimelineRoomInfo +import io.element.android.features.messages.impl.timeline.TimelineEvents +import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent +import io.element.android.features.messages.impl.timeline.model.event.canBeRepliedTo +import io.element.android.features.messages.impl.timeline.session.SessionState +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.UserId + +@Composable +internal fun TimelineItemRow( + timelineItem: TimelineItem, + timelineRoomInfo: TimelineRoomInfo, + showReadReceipts: Boolean, + isLastOutgoingMessage: Boolean, + highlightedItem: String?, + userHasPermissionToSendMessage: Boolean, + sessionState: SessionState, + onUserDataClick: (UserId) -> Unit, + onClick: (TimelineItem.Event) -> Unit, + onLongClick: (TimelineItem.Event) -> Unit, + inReplyToClick: (EventId) -> Unit, + onReactionClick: (key: String, TimelineItem.Event) -> Unit, + onReactionLongClick: (key: String, TimelineItem.Event) -> Unit, + onMoreReactionsClick: (TimelineItem.Event) -> Unit, + onReadReceiptClick: (TimelineItem.Event) -> Unit, + onTimestampClicked: (TimelineItem.Event) -> Unit, + onSwipeToReply: (TimelineItem.Event) -> Unit, + eventSink: (TimelineEvents) -> Unit, + modifier: Modifier = Modifier +) { + when (timelineItem) { + is TimelineItem.Virtual -> { + TimelineItemVirtualRow( + virtual = timelineItem, + sessionState = sessionState, + modifier = modifier, + ) + } + is TimelineItem.Event -> { + if (timelineItem.content is TimelineItemStateContent) { + TimelineItemStateEventRow( + event = timelineItem, + showReadReceipts = showReadReceipts, + isLastOutgoingMessage = isLastOutgoingMessage, + isHighlighted = highlightedItem == timelineItem.identifier(), + onClick = { onClick(timelineItem) }, + onReadReceiptsClick = onReadReceiptClick, + onLongClick = { onLongClick(timelineItem) }, + eventSink = eventSink, + modifier = modifier, + ) + } else { + TimelineItemEventRow( + event = timelineItem, + timelineRoomInfo = timelineRoomInfo, + showReadReceipts = showReadReceipts, + isLastOutgoingMessage = isLastOutgoingMessage, + isHighlighted = highlightedItem == timelineItem.identifier(), + canReply = userHasPermissionToSendMessage && timelineItem.content.canBeRepliedTo(), + onClick = { onClick(timelineItem) }, + onLongClick = { onLongClick(timelineItem) }, + onUserDataClick = onUserDataClick, + inReplyToClick = inReplyToClick, + onReactionClick = onReactionClick, + onReactionLongClick = onReactionLongClick, + onMoreReactionsClick = onMoreReactionsClick, + onReadReceiptClick = onReadReceiptClick, + onTimestampClicked = onTimestampClicked, + onSwipeToReply = { onSwipeToReply(timelineItem) }, + eventSink = eventSink, + modifier = modifier, + ) + } + } + is TimelineItem.GroupedEvents -> { + TimelineItemGroupedEventsRow( + timelineItem = timelineItem, + timelineRoomInfo = timelineRoomInfo, + showReadReceipts = showReadReceipts, + isLastOutgoingMessage = isLastOutgoingMessage, + highlightedItem = highlightedItem, + sessionState = sessionState, + onClick = onClick, + onLongClick = onLongClick, + inReplyToClick = inReplyToClick, + onUserDataClick = onUserDataClick, + onTimestampClicked = onTimestampClicked, + onReactionClick = onReactionClick, + onReactionLongClick = onReactionLongClick, + onMoreReactionsClick = onMoreReactionsClick, + onReadReceiptClick = onReadReceiptClick, + eventSink = eventSink, + modifier = modifier, + ) + } + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt index ccffcc16ca..b2557b378d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt @@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.timeline.components import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.widthIn @@ -32,50 +33,69 @@ import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.aTimelineItemEvent import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView import io.element.android.features.messages.impl.timeline.components.event.noExtraPadding +import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewState +import io.element.android.features.messages.impl.timeline.components.receipt.TimelineItemReadReceiptView +import io.element.android.features.messages.impl.timeline.components.receipt.aReadReceiptData import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition +import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent import io.element.android.features.messages.impl.timeline.util.defaultTimelineContentPadding import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import kotlinx.collections.immutable.toPersistentList @Composable fun TimelineItemStateEventRow( event: TimelineItem.Event, + showReadReceipts: Boolean, + isLastOutgoingMessage: Boolean, isHighlighted: Boolean, onClick: () -> Unit, onLongClick: () -> Unit, + onReadReceiptsClick: (event: TimelineItem.Event) -> Unit, eventSink: (TimelineEvents) -> Unit, modifier: Modifier = Modifier ) { val interactionSource = remember { MutableInteractionSource() } - Box( + Column( modifier = modifier .fillMaxWidth() - .padding(vertical = 8.dp) - .wrapContentHeight(), - contentAlignment = Alignment.Center ) { - MessageStateEventContainer( - isHighlighted = isHighlighted, - interactionSource = interactionSource, - onClick = onClick, - onLongClick = onLongClick, + Box( modifier = Modifier - .zIndex(-1f) - .widthIn(max = 320.dp) + .fillMaxWidth() + .padding(top = 8.dp, bottom = 2.dp) + .wrapContentHeight(), + contentAlignment = Alignment.Center ) { - TimelineItemEventContentView( - content = event.content, - isMine = event.isMine, + MessageStateEventContainer( + isHighlighted = isHighlighted, interactionSource = interactionSource, onClick = onClick, onLongClick = onLongClick, - extraPadding = noExtraPadding, - eventSink = eventSink, - modifier = Modifier.defaultTimelineContentPadding() - ) + modifier = Modifier + .zIndex(-1f) + .widthIn(max = 320.dp) + ) { + TimelineItemEventContentView( + content = event.content, + onLinkClicked = {}, + extraPadding = noExtraPadding, + eventSink = eventSink, + modifier = Modifier.defaultTimelineContentPadding() + ) + } } + TimelineItemReadReceiptView( + state = ReadReceiptViewState( + sendState = event.localSendState, + isLastOutgoingMessage = isLastOutgoingMessage, + receipts = event.readReceiptState.receipts, + ), + showReadReceipts = showReadReceipts, + onReadReceiptsClicked = { onReadReceiptsClick(event) }, + ) } } @@ -86,11 +106,17 @@ internal fun TimelineItemStateEventRowPreview() = ElementPreview { event = aTimelineItemEvent( isMine = false, content = aTimelineItemStateEventContent(), - groupPosition = TimelineItemGroupPosition.None + groupPosition = TimelineItemGroupPosition.None, + readReceiptState = TimelineItemReadReceipts( + receipts = listOf(aReadReceiptData(0)).toPersistentList(), + ) ), + showReadReceipts = true, + isLastOutgoingMessage = false, isHighlighted = false, onClick = {}, onLongClick = {}, + onReadReceiptsClick = {}, eventSink = {} ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiItem.kt index 3de0734b61..8b720ce390 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiItem.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiItem.kt @@ -22,20 +22,28 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.sizeIn import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.ripple.rememberRipple +import androidx.compose.material3.LocalTextStyle import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.clearAndSetSemantics +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.element.android.compound.theme.ElementTheme import io.element.android.emojibasebindings.Emoji import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.text.toDp import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme +import io.element.android.libraries.ui.strings.CommonStrings @Composable fun EmojiItem( @@ -43,28 +51,36 @@ fun EmojiItem( isSelected: Boolean, onEmojiSelected: (Emoji) -> Unit, modifier: Modifier = Modifier, + emojiSize: TextUnit = 20.sp, ) { val backgroundColor = if (isSelected) { ElementTheme.colors.bgActionPrimaryRest } else { Color.Transparent } - + val description = if (isSelected) { + stringResource(id = CommonStrings.a11y_remove_reaction_with, item.unicode) + } else { + stringResource(id = CommonStrings.a11y_react_with, item.unicode) + } Box( modifier = modifier - .size(40.dp) + .sizeIn(minWidth = 40.dp, minHeight = 40.dp) .background(backgroundColor, CircleShape) .clickable( enabled = true, onClick = { onEmojiSelected(item) }, - indication = rememberRipple(bounded = false, radius = 20.dp), + indication = rememberRipple(bounded = false, radius = emojiSize.toDp() / 2 + 10.dp), interactionSource = remember { MutableInteractionSource() } - ), + ) + .clearAndSetSemantics { + contentDescription = description + }, contentAlignment = Alignment.Center ) { Text( text = item.unicode, - style = ElementTheme.typography.fontHeadingSmRegular, + style = LocalTextStyle.current.copy(fontSize = emojiSize), ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiPicker.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiPicker.kt index 02ddfec026..ddf524b656 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiPicker.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiPicker.kt @@ -20,6 +20,7 @@ import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.lazy.grid.GridCells @@ -43,6 +44,7 @@ import io.element.android.emojibasebindings.EmojibaseDatasource import io.element.android.emojibasebindings.EmojibaseStore import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.text.toSp import io.element.android.libraries.designsystem.theme.components.Icon import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.persistentSetOf @@ -65,7 +67,7 @@ fun EmojiPicker( ) { EmojibaseCategory.entries.forEachIndexed { index, category -> Tab( - text = { + icon = { Icon( imageVector = category.icon, contentDescription = stringResource(id = category.title) @@ -87,15 +89,18 @@ fun EmojiPicker( val emojis = categories[category] ?: listOf() LazyVerticalGrid( modifier = Modifier.fillMaxSize(), - columns = GridCells.Adaptive(minSize = 40.dp), + columns = GridCells.Adaptive(minSize = 48.dp), contentPadding = PaddingValues(vertical = 10.dp, horizontal = 16.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(2.dp) ) { items(emojis, key = { it.unicode }) { item -> EmojiItem( + modifier = Modifier.aspectRatio(1f), item = item, isSelected = selectedEmojis.contains(item.unicode), - onEmojiSelected = onEmojiSelected + onEmojiSelected = onEmojiSelected, + emojiSize = 32.dp.toSp(), ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/ExtraPadding.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/ExtraPadding.kt index e5851c993b..e0be0f0662 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/ExtraPadding.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/ExtraPadding.kt @@ -16,25 +16,30 @@ package io.element.android.features.messages.impl.timeline.components.event +import androidx.compose.material3.LocalTextStyle import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextMeasurer +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.rememberTextMeasurer +import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.times +import io.element.android.compound.theme.ElementTheme import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent import io.element.android.libraries.core.bool.orFalse -import io.element.android.libraries.designsystem.text.toDp import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings +import kotlin.math.ceil // Allow to not overlap the timestamp with the text, in the message bubble. // Compute the size of the worst case. -data class ExtraPadding(val nbChars: Int) +data class ExtraPadding(val extraWidth: Dp) -val noExtraPadding = ExtraPadding(0) +val noExtraPadding = ExtraPadding(0.dp) /** * See [io.element.android.features.messages.impl.timeline.components.TimelineEventTimestampView] for the related View. @@ -46,37 +51,48 @@ fun TimelineItem.Event.toExtraPadding(): ExtraPadding { val hasMessageSendingFailed = localSendState is LocalEventSendState.SendingFailed val isMessageEdited = (content as? TimelineItemTextBasedContent)?.isEdited.orFalse() - var strLen = 6 + val textMeasurer = rememberTextMeasurer(cacheSize = 128) + val density = LocalDensity.current + + var strLen = 2.dp // Extra space char if (isMessageEdited) { - strLen += stringResource(id = CommonStrings.common_edited_suffix).length + 3 + val editedText = stringResource(id = CommonStrings.common_edited_suffix) + val extraLen = remember(editedText, density) { textMeasurer.getExtraPadding(editedText, density) } + 10.dp // Text + spacing + strLen += extraLen } - strLen += formattedTime.length + strLen += remember(formattedTime, density) { textMeasurer.getExtraPadding(formattedTime, density) } if (hasMessageSendingFailed) { - strLen += 5 + strLen += 19.dp // Image + spacing + // I do not know why, but adding extra widths avoid overlapping when the + // message is edited and in error. if (isMessageEdited) { - // I do not know why, but adding 2 more chars avoid overlapping when the - // message is edited and in error. - strLen += 2 + strLen += 2.dp } } return ExtraPadding(strLen) } +private fun TextMeasurer.getExtraPadding(text: String, density: Density): Dp { + val timestampTextStyle = ElementTheme.typography.fontBodyXsRegular + val textWidth = measure(text = text, style = timestampTextStyle).size.width + return (textWidth/density.density).dp +} + /** * Get a string to add to the content of the message to avoid overlapping the timestamp. - * @param fontSize the font size of the message content, to be able to add more space char if the font is small. */ -fun ExtraPadding.getStr(fontSize: TextUnit): String { - if (nbChars == 0) return "" - val timestampFontSize = ElementTheme.typography.fontBodyXsRegular.fontSize // 11.sp - val nbOfSpaces = (timestampFontSize.value / fontSize.value * nbChars).toInt() + 1 - // A space and some unbreakable spaces - return " " + "\u00A0".repeat(nbOfSpaces) +@Composable +fun ExtraPadding.getStr(textStyle: TextStyle = LocalTextStyle.current): String { + if (extraWidth == 0.dp) return "" + val density = LocalDensity.current + val textMeasurer = rememberTextMeasurer(128) + val charWidth = remember(textStyle) { textMeasurer.measure(text = "\u00A0", style = textStyle).size.width } + val widthPx = remember(density, extraWidth) { with(density) { extraWidth.toPx() } } + // A space and some unbreakable spaces, always rounding the result to the next value if not a integer + return " " + "\u00A0".repeat(ceil(widthPx / charWidth).toInt()) } @Composable fun ExtraPadding.getDpSize(): Dp { - if (nbChars == 0) return 0.dp - val timestampFontSize = ElementTheme.typography.fontBodyXsRegular.fontSize // 11.sp - return nbChars * timestampFontSize.toDp() / 3 + return extraWidth } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAspectRatioBox.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAspectRatioBox.kt index f733913bc8..bf30669a25 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAspectRatioBox.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAspectRatioBox.kt @@ -25,9 +25,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -private const val MAX_HEIGHT_IN_DP = 360f -private const val MIN_ASPECT_RATIO = 0.6f -private const val MAX_ASPECT_RATIO = 4f +private const val MIN_HEIGHT_IN_DP = 100 +private const val MAX_HEIGHT_IN_DP = 360 private const val DEFAULT_ASPECT_RATIO = 1.33f @Composable @@ -37,11 +36,11 @@ fun TimelineItemAspectRatioBox( contentAlignment: Alignment = Alignment.TopStart, content: @Composable (BoxScope.() -> Unit), ) { - val safeAspectRatio = (aspectRatio ?: DEFAULT_ASPECT_RATIO).coerceIn(MIN_ASPECT_RATIO, MAX_ASPECT_RATIO) + val safeAspectRatio = aspectRatio ?: DEFAULT_ASPECT_RATIO Box( modifier = modifier - .heightIn(max = MAX_HEIGHT_IN_DP.dp) - .aspectRatio(safeAspectRatio, true), + .heightIn(min = MIN_HEIGHT_IN_DP.dp, max = MAX_HEIGHT_IN_DP.dp) + .aspectRatio(safeAspectRatio, false), contentAlignment = contentAlignment, content = content ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAudioView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAudioView.kt index b9a1cf622a..257edbe28c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAudioView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAudioView.kt @@ -33,14 +33,13 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp +import io.element.android.compound.theme.ElementTheme import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContentProvider -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme @Composable fun TimelineItemAudioView( @@ -76,7 +75,7 @@ fun TimelineItemAudioView( overflow = TextOverflow.Ellipsis ) Text( - text = content.fileExtensionAndSize + extraPadding.getStr(12.sp), + text = content.fileExtensionAndSize + extraPadding.getStr(ElementTheme.typography.fontBodySmRegular), color = ElementTheme.materialColors.secondary, style = ElementTheme.typography.fontBodySmRegular, maxLines = 1, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEventContentView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEventContentView.kt index 22d1d9cacd..82f1af4858 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEventContentView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEventContentView.kt @@ -16,7 +16,6 @@ package io.element.android.features.messages.impl.timeline.components.event -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import io.element.android.features.messages.impl.timeline.TimelineEvents @@ -41,11 +40,8 @@ import io.element.android.libraries.architecture.Presenter @Composable fun TimelineItemEventContentView( content: TimelineItemEventContent, - isMine: Boolean, - interactionSource: MutableInteractionSource, extraPadding: ExtraPadding, - onClick: () -> Unit, - onLongClick: () -> Unit, + onLinkClicked: (url: String) -> Unit, eventSink: (TimelineEvents) -> Unit, modifier: Modifier = Modifier ) { @@ -64,10 +60,8 @@ fun TimelineItemEventContentView( is TimelineItemTextBasedContent -> TimelineItemTextView( content = content, extraPadding = extraPadding, - interactionSource = interactionSource, modifier = modifier, - onTextClicked = onClick, - onTextLongClicked = onLongClick + onLinkClicked = onLinkClicked, ) is TimelineItemUnknownContent -> TimelineItemUnknownView( content = content, @@ -102,7 +96,6 @@ fun TimelineItemEventContentView( ) is TimelineItemPollContent -> TimelineItemPollView( content = content, - isMine = isMine, eventSink = eventSink, modifier = modifier, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt index a8fb3e1fcc..f0dd10763a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt @@ -32,15 +32,14 @@ import androidx.compose.ui.draw.rotate import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp +import io.element.android.compound.theme.ElementTheme import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContentProvider -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme @Composable fun TimelineItemFileView( @@ -60,7 +59,7 @@ fun TimelineItemFileView( ) { Icon( resourceId = CommonDrawables.ic_attachment, - contentDescription = "OpenFile", + contentDescription = null, tint = ElementTheme.materialColors.primary, modifier = Modifier .size(16.dp) @@ -77,7 +76,7 @@ fun TimelineItemFileView( overflow = TextOverflow.Ellipsis ) Text( - text = content.fileExtensionAndSize + extraPadding.getStr(12.sp), + text = content.fileExtensionAndSize + extraPadding.getStr(textStyle = ElementTheme.typography.fontBodySmRegular), color = ElementTheme.materialColors.secondary, style = ElementTheme.typography.fontBodySmRegular, maxLines = 1, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt index b5856e1ce7..7bebd9e6d7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt @@ -18,13 +18,12 @@ package io.element.android.features.messages.impl.timeline.components.event import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContentProvider import io.element.android.libraries.designsystem.components.BlurHashAsyncImage -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.ui.media.MediaRequestData @Composable @@ -34,12 +33,11 @@ fun TimelineItemImageView( ) { TimelineItemAspectRatioBox( aspectRatio = content.aspectRatio, - modifier = modifier + modifier = modifier, ) { BlurHashAsyncImage( model = MediaRequestData(content.preferredMediaSource, MediaRequestData.Kind.File(content.body, content.mimeType)), blurHash = content.blurhash, - contentScale = ContentScale.Crop, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemInformativeView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemInformativeView.kt index 1ff45e428e..a81e928f18 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemInformativeView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemInformativeView.kt @@ -27,13 +27,12 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.designsystem.icons.CompoundDrawables import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme @Composable fun TimelineItemInformativeView( @@ -58,7 +57,7 @@ fun TimelineItemInformativeView( fontStyle = FontStyle.Italic, color = MaterialTheme.colorScheme.secondary, style = ElementTheme.typography.fontBodyMdRegular, - text = text + extraPadding.getStr(14.sp) + text = text + extraPadding.getStr(textStyle = ElementTheme.typography.fontBodyMdRegular) ) } } @@ -69,7 +68,7 @@ internal fun TimelineItemInformativeViewPreview() = ElementPreview { TimelineItemInformativeView( text = "Info", iconDescription = "", - iconResourceId = CommonDrawables.ic_compound_delete, + iconResourceId = CompoundDrawables.ic_delete, extraPadding = noExtraPadding, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt index 958845f98c..91dd5b99d3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt @@ -22,7 +22,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContentProvider -import io.element.android.features.poll.api.PollContentView +import io.element.android.features.poll.api.pollcontent.PollContentView import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.api.core.EventId @@ -31,7 +31,6 @@ import kotlinx.collections.immutable.toImmutableList @Composable fun TimelineItemPollView( content: TimelineItemPollContent, - isMine: Boolean, eventSink: (TimelineEvents) -> Unit, modifier: Modifier = Modifier, ) { @@ -43,15 +42,20 @@ fun TimelineItemPollView( eventSink(TimelineEvents.PollEndClicked(pollStartId)) } + fun onPollEdit(pollStartId: EventId) { + eventSink(TimelineEvents.PollEditClicked(pollStartId)) + } + PollContentView( eventId = content.eventId, question = content.question, answerItems = content.answerItems.toImmutableList(), pollKind = content.pollKind, isPollEnded = content.isEnded, - isMine = isMine, + isPollEditable = content.isEditable, + isMine = content.isMine, onAnswerSelected = ::onAnswerSelected, - onPollEdit = {}, // TODO Polls: Wire up this callback once poll edit screen is done. + onPollEdit = ::onPollEdit, onPollEnd = ::onPollEnd, modifier = modifier, ) @@ -63,18 +67,6 @@ internal fun TimelineItemPollViewPreview(@PreviewParameter(TimelineItemPollConte ElementPreview { TimelineItemPollView( content = content, - isMine = false, - eventSink = {}, - ) - } - -@PreviewsDayNight -@Composable -internal fun TimelineItemPollCreatorViewPreview(@PreviewParameter(TimelineItemPollContentProvider::class) content: TimelineItemPollContent) = - ElementPreview { - TimelineItemPollView( - content = content, - isMine = true, eventSink = {}, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemRedactedView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemRedactedView.kt index cb0fbde6df..7ff7a23e7e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemRedactedView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemRedactedView.kt @@ -20,9 +20,9 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent -import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.icons.CompoundDrawables import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.utils.CommonDrawables +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -34,7 +34,7 @@ fun TimelineItemRedactedView( TimelineItemInformativeView( text = stringResource(id = CommonStrings.common_message_removed), iconDescription = stringResource(id = CommonStrings.common_message_removed), - iconResourceId = CommonDrawables.ic_compound_delete, + iconResourceId = CompoundDrawables.ic_delete, extraPadding = extraPadding, modifier = modifier ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStateView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStateView.kt index fe7b7edeaa..7d89ec3993 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStateView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStateView.kt @@ -25,7 +25,7 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable fun TimelineItemStateView( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemTextView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemTextView.kt index 4282f0253b..dfe46d09f9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemTextView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemTextView.kt @@ -16,56 +16,53 @@ package io.element.android.features.messages.impl.timeline.components.event -import androidx.compose.foundation.interaction.MutableInteractionSource +import android.text.SpannableString import androidx.compose.foundation.layout.Box import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.LocalTextStyle import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.sp -import io.element.android.features.messages.impl.timeline.components.html.HtmlDocument +import androidx.compose.ui.unit.dp +import androidx.core.text.buildSpannedString +import io.element.android.compound.theme.ElementTheme import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContentProvider -import io.element.android.libraries.designsystem.components.ClickableLinkText -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.text.toAnnotatedString -import io.element.android.libraries.theme.ElementTheme +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.textcomposer.ElementRichTextEditorStyle +import io.element.android.wysiwyg.compose.EditorStyledText @Composable fun TimelineItemTextView( content: TimelineItemTextBasedContent, - interactionSource: MutableInteractionSource, extraPadding: ExtraPadding, + onLinkClicked: (String) -> Unit, modifier: Modifier = Modifier, - onTextClicked: () -> Unit = {}, - onTextLongClicked: () -> Unit = {}, ) { - CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.textPrimary) { - val htmlDocument = content.htmlDocument - if (htmlDocument != null) { - HtmlDocument( - document = htmlDocument, - extraPadding = extraPadding, - modifier = modifier, - onTextClicked = onTextClicked, - onTextLongClicked = onTextLongClicked, - interactionSource = interactionSource - ) - } else { - Box(modifier) { - val textWithPadding = remember(content.body) { - content.body + extraPadding.getStr(16.sp).toAnnotatedString() + CompositionLocalProvider( + LocalContentColor provides ElementTheme.colors.textPrimary, + LocalTextStyle provides ElementTheme.typography.fontBodyLgRegular + ) { + + val formattedBody = content.formattedBody + val body = SpannableString(formattedBody ?: content.body) + val extraPaddingText = extraPadding.getStr() + + Box(modifier) { + val textWithPadding = remember(body) { + buildSpannedString { + append(body) + append(extraPaddingText) } - ClickableLinkText( - text = textWithPadding, - onClick = onTextClicked, - onLongClick = onTextLongClicked, - interactionSource = interactionSource - ) } + EditorStyledText( + text = textWithPadding, + onLinkClickedListener = onLinkClicked, + style = ElementRichTextEditorStyle.textStyle(), + ) } } } @@ -77,7 +74,7 @@ internal fun TimelineItemTextViewPreview( ) = ElementPreview { TimelineItemTextView( content = content, - interactionSource = remember { MutableInteractionSource() }, - extraPadding = ExtraPadding(nbChars = 8), + extraPadding = ExtraPadding(extraWidth = 32.dp), + onLinkClicked = {}, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemUnknownView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemUnknownView.kt index 182f90b23b..63fc5fd00b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemUnknownView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemUnknownView.kt @@ -20,9 +20,9 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent -import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.icons.CompoundDrawables import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.utils.CommonDrawables +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -34,7 +34,7 @@ fun TimelineItemUnknownView( TimelineItemInformativeView( text = stringResource(id = CommonStrings.common_unsupported_event), iconDescription = stringResource(id = CommonStrings.dialog_title_warning), - iconResourceId = CommonDrawables.ic_compound_info_solid, + iconResourceId = CompoundDrawables.ic_info_solid, extraPadding = extraPadding, modifier = modifier ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt index f666a29ac9..87685e432c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContentProvider @@ -34,6 +35,7 @@ import io.element.android.libraries.designsystem.modifiers.roundedBackground import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.matrix.ui.media.MediaRequestData +import io.element.android.libraries.ui.strings.CommonStrings @Composable fun TimelineItemVideoView( @@ -56,7 +58,7 @@ fun TimelineItemVideoView( ) { Image( Icons.Default.PlayArrow, - contentDescription = "Play", + contentDescription = stringResource(id = CommonStrings.a11y_play), colorFilter = ColorFilter.tint(Color.White), ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt index 6d3dd57994..ed7e031392 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt @@ -42,7 +42,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp -import io.element.android.features.messages.impl.R +import io.element.android.compound.theme.ElementTheme import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContentProvider import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessageEvents @@ -56,7 +56,7 @@ import io.element.android.libraries.designsystem.theme.components.CircularProgre import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme +import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.delay @@ -119,7 +119,7 @@ private fun PlayButton( enabled = enabled, ) { Icon( - resourceId = R.drawable.play, + resourceId = CommonDrawables.ic_play, contentDescription = stringResource(id = CommonStrings.a11y_play), ) } @@ -133,8 +133,8 @@ private fun PauseButton( onClick = onClick, ) { Icon( - resourceId = R.drawable.pause, - contentDescription = stringResource(id = CommonStrings.a11y_play), + resourceId = CommonDrawables.ic_pause, + contentDescription = stringResource(id = CommonStrings.a11y_pause), ) } } @@ -147,7 +147,7 @@ private fun RetryButton( onClick = onClick, ) { Icon( - resourceId = R.drawable.retry, + resourceId = CommonDrawables.ic_retry, contentDescription = stringResource(id = CommonStrings.action_retry), ) } @@ -183,7 +183,7 @@ private fun ProgressButton( ) } else { Icon( - resourceId = R.drawable.pause, + resourceId = CommonDrawables.ic_pause, contentDescription = stringResource(id = CommonStrings.a11y_pause), ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt index ebe1abaf17..1d50509844 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt @@ -16,6 +16,8 @@ package io.element.android.features.messages.impl.timeline.components.group +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -26,19 +28,20 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme private val CORNER_RADIUS = 8.dp @@ -76,9 +79,17 @@ fun GroupHeaderView( color = MaterialTheme.colorScheme.secondary, style = ElementTheme.typography.fontBodyMdRegular, ) + val rotation: Float by animateFloatAsState( + targetValue = if (isExpanded) 90f else 0f, + animationSpec = tween( + delayMillis = 0, + durationMillis = 300, + ), + label = "chevron" + ) Icon( - modifier = Modifier.rotate(if (isExpanded) 180f else 0f), - resourceId = CommonDrawables.ic_compound_chevron_down, + modifier = Modifier.rotate(rotation), + imageVector = CompoundIcons.ChevronRight, contentDescription = null, tint = MaterialTheme.colorScheme.secondary ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/DocumentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/DocumentProvider.kt deleted file mode 100644 index 768b0e3ec3..0000000000 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/DocumentProvider.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components.html - -import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import org.jsoup.Jsoup -import org.jsoup.nodes.Document - -open class DocumentProvider : PreviewParameterProvider { - override val values: Sequence - get() = sequenceOf( - "text", - "Strong", - "Bold", - "Italic", - // FIXME This does not work - "Bold then italic", - // FIXME This does not work - "Italic then bold", - "em", - "unknown", - // FIXME `br` is not rendered correctly in the Preview. - "Line 1
Line 2", - "code", - "del", - "

Heading 1

Heading 2

Heading 3

Heading 4

Heading 5
Heading 6
Heading 7", - "link", - "

paragraph

", - "

paragraph 1

paragraph 2

", - "
  1. ol item 1
  2. ol item 2
", - "
  1. ol item 1 italic
  2. ol item 2 bold
", - "
  • ul item 1
  • ul item 2
", - "
blockquote
", - // TODO Find a way to make is work with `pre`. For now there is an error with - // jsoup: java.lang.NoSuchMethodError: 'org.jsoup.nodes.Element org.jsoup.nodes.Element.firstElementChild()' - // "
pre
", - "
In reply to " + - "@alice:matrix.org
original message
reply", - "
  1. Testing link item.
  2. And another item.
", - "", - ).map { Jsoup.parse(it) } -} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt deleted file mode 100644 index ad6d2790b0..0000000000 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt +++ /dev/null @@ -1,623 +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. - */ - -@file:OptIn(ExperimentalLayoutApi::class) - -package io.element.android.features.messages.impl.timeline.components.html - -import androidx.compose.foundation.background -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ExperimentalLayoutApi -import androidx.compose.foundation.layout.FlowRow -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.InlineTextContent -import androidx.compose.foundation.text.appendInlineContent -import androidx.compose.material3.ColorScheme -import androidx.compose.material3.LocalTextStyle -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.drawBehind -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextDecoration -import androidx.compose.ui.text.withStyle -import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import io.element.android.features.messages.impl.timeline.components.event.ExtraPadding -import io.element.android.features.messages.impl.timeline.components.event.getDpSize -import io.element.android.features.messages.impl.timeline.components.event.noExtraPadding -import io.element.android.libraries.designsystem.components.ClickableLinkText -import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.text.toDp -import io.element.android.libraries.designsystem.theme.components.Surface -import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.matrix.api.permalink.PermalinkData -import io.element.android.libraries.matrix.api.permalink.PermalinkParser -import io.element.android.libraries.theme.ElementTheme -import io.element.android.libraries.theme.LinkColor -import kotlinx.collections.immutable.persistentMapOf -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import org.jsoup.nodes.Node -import org.jsoup.nodes.TextNode - -private const val CHIP_ID = "chip" - -@Composable -fun HtmlDocument( - document: Document, - extraPadding: ExtraPadding, - interactionSource: MutableInteractionSource, - modifier: Modifier = Modifier, - onTextClicked: () -> Unit = {}, - onTextLongClicked: () -> Unit = {}, -) { - FlowRow( - modifier = modifier, - ) { - HtmlBody( - body = document.body(), - interactionSource = interactionSource, - onTextClicked = onTextClicked, - onTextLongClicked = onTextLongClicked, - ) - Spacer( - modifier = Modifier.size( - width = extraPadding.getDpSize(), - height = ElementTheme.typography.fontBodyXsRegular.fontSize.toDp() * 1.25f - ) - ) - } -} - -@Composable -private fun HtmlBody( - body: Element, - interactionSource: MutableInteractionSource, - modifier: Modifier = Modifier, - onTextClicked: () -> Unit = {}, - onTextLongClicked: () -> Unit = {}, -) { - @Composable - fun NodesFlowRode( - nodes: Iterator, - interactionSource: MutableInteractionSource, - onTextClicked: () -> Unit = {}, - onTextLongClicked: () -> Unit = {}, - ) = FlowRow( - horizontalArrangement = Arrangement.spacedBy(2.dp, Alignment.Start), - verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.Top), - ) { - var sameRow = true - while (sameRow && nodes.hasNext()) { - when (val node = nodes.next()) { - is TextNode -> { - if (!node.isBlank) { - ClickableLinkText( - text = node.text(), - interactionSource = interactionSource, - onClick = onTextClicked, - onLongClick = onTextLongClicked, - ) - } - } - is Element -> { - if (node.isInline()) { - HtmlInline( - node, - onTextClicked = onTextClicked, - onTextLongClicked = onTextLongClicked, - interactionSource = interactionSource - ) - } else { - HtmlBlock( - element = node, - onTextClicked = onTextClicked, - onTextLongClicked = onTextLongClicked, - interactionSource = interactionSource - ) - sameRow = false - } - } - else -> continue - } - } - } - - Column(modifier = modifier) { - val nodesIterator = body.childNodes().iterator() - while (nodesIterator.hasNext()) { - NodesFlowRode( - nodes = nodesIterator, - interactionSource = interactionSource, - onTextClicked = onTextClicked, - onTextLongClicked = onTextLongClicked, - ) - } - } -} - -private fun Element.isInline(): Boolean { - return when (tagName().lowercase()) { - "del" -> true - "mx-reply" -> false - else -> !isBlock - } -} - -@Composable -private fun HtmlBlock( - element: Element, - interactionSource: MutableInteractionSource, - modifier: Modifier = Modifier, - onTextClicked: () -> Unit = {}, - onTextLongClicked: () -> Unit = {}, -) { - val blockModifier = modifier - .padding(top = 4.dp) - when (element.tagName().lowercase()) { - "p" -> HtmlParagraph( - paragraph = element, - modifier = blockModifier, - onTextClicked = onTextClicked, - onTextLongClicked = onTextLongClicked, - interactionSource = interactionSource - ) - "h1", "h2", "h3", "h4", "h5", "h6" -> HtmlHeading( - heading = element, - modifier = blockModifier, - onTextClicked = onTextClicked, - onTextLongClicked = onTextLongClicked, - interactionSource = interactionSource - ) - "ol" -> HtmlOrderedList( - orderedList = element, - modifier = blockModifier, - onTextClicked = onTextClicked, - onTextLongClicked = onTextLongClicked, - interactionSource = interactionSource - ) - "ul" -> HtmlUnorderedList( - unorderedList = element, - modifier = blockModifier, - onTextClicked = onTextClicked, - onTextLongClicked = onTextLongClicked, - interactionSource = interactionSource - ) - "blockquote" -> HtmlBlockquote( - blockquote = element, - modifier = blockModifier, - onTextClicked = onTextClicked, - onTextLongClicked = onTextLongClicked, - interactionSource = interactionSource - ) - "pre" -> HtmlPreformatted(element, blockModifier) - "mx-reply" -> HtmlMxReply( - mxReply = element, - modifier = blockModifier, - onTextClicked = onTextClicked, - onTextLongClicked = onTextLongClicked, - interactionSource = interactionSource - ) - else -> return - } -} - -@Composable -private fun HtmlInline( - element: Element, - interactionSource: MutableInteractionSource, - modifier: Modifier = Modifier, - onTextClicked: () -> Unit = {}, - onTextLongClicked: () -> Unit = {}, -) { - Box(modifier) { - val styledText = buildAnnotatedString { - appendInlineElement(element, MaterialTheme.colorScheme) - } - HtmlText( - text = styledText, - onClick = onTextClicked, - onLongClick = onTextLongClicked, - interactionSource = interactionSource - ) - } -} - -@Composable -private fun HtmlPreformatted( - pre: Element, - modifier: Modifier = Modifier -) { - val isCode = pre.firstElementChild()?.tagName()?.lowercase() == "code" - val backgroundColor = - if (isCode) MaterialTheme.colorScheme.codeBackground() else Color.Unspecified - Box( - modifier - .background(color = backgroundColor) - .padding(horizontal = 8.dp) - ) { - Text( - text = pre.wholeText(), - style = TextStyle(fontFamily = FontFamily.Monospace), - color = MaterialTheme.colorScheme.primary, - ) - } -} - -@Composable -private fun HtmlParagraph( - paragraph: Element, - interactionSource: MutableInteractionSource, - modifier: Modifier = Modifier, - onTextClicked: () -> Unit = {}, - onTextLongClicked: () -> Unit = {}, -) { - Box(modifier) { - val styledText = buildAnnotatedString { - appendInlineChildrenElements(paragraph.childNodes(), MaterialTheme.colorScheme) - } - HtmlText( - text = styledText, onClick = onTextClicked, - onLongClick = onTextLongClicked, interactionSource = interactionSource - ) - } -} - -@Composable -private fun HtmlBlockquote( - blockquote: Element, - interactionSource: MutableInteractionSource, - modifier: Modifier = Modifier, - onTextClicked: () -> Unit = {}, - onTextLongClicked: () -> Unit = {}, -) { - val color = MaterialTheme.colorScheme.onBackground - Box( - modifier = modifier - .drawBehind { - drawLine( - color = color, - strokeWidth = 2f, - start = Offset(12.dp.value, 0f), - end = Offset(12.dp.value, size.height) - ) - } - .padding(start = 8.dp, top = 4.dp, bottom = 4.dp) - ) { - val text = buildAnnotatedString { - withStyle(style = SpanStyle(fontStyle = FontStyle.Italic)) { - appendInlineChildrenElements(blockquote.childNodes(), MaterialTheme.colorScheme) - } - } - HtmlText( - text = text, onClick = onTextClicked, - onLongClick = onTextLongClicked, interactionSource = interactionSource - ) - } -} - -@Composable -private fun HtmlHeading( - heading: Element, - interactionSource: MutableInteractionSource, - modifier: Modifier = Modifier, - onTextClicked: () -> Unit = {}, - onTextLongClicked: () -> Unit = {}, -) { - val style = when (heading.tagName().lowercase()) { - "h1" -> MaterialTheme.typography.headlineLarge.copy(fontSize = 30.sp) - "h2" -> MaterialTheme.typography.headlineLarge.copy(fontSize = 26.sp) - "h3" -> MaterialTheme.typography.headlineMedium.copy(fontSize = 22.sp) - "h4" -> MaterialTheme.typography.headlineMedium.copy(fontSize = 18.sp) - "h5" -> MaterialTheme.typography.headlineSmall.copy(fontSize = 14.sp) - "h6" -> MaterialTheme.typography.headlineSmall.copy(fontSize = 12.sp) - else -> { - return - } - } - Box(modifier) { - val text = buildAnnotatedString { - appendInlineChildrenElements(heading.childNodes(), MaterialTheme.colorScheme) - } - HtmlText( - text = text, - style = style, - onClick = onTextClicked, - onLongClick = onTextLongClicked, - interactionSource = interactionSource - ) - } -} - -@Composable -private fun HtmlMxReply( - mxReply: Element, - interactionSource: MutableInteractionSource, - modifier: Modifier = Modifier, - onTextClicked: () -> Unit = {}, - onTextLongClicked: () -> Unit = {}, -) { - val blockquote = mxReply.childNodes().firstOrNull() ?: return - val shape = RoundedCornerShape(12.dp) - Surface( - modifier = modifier - .padding(bottom = 4.dp) - .offset(x = (-8).dp), - color = MaterialTheme.colorScheme.background, - shape = shape, - ) { - val text = buildAnnotatedString { - for (blockquoteNode in blockquote.childNodes()) { - when (blockquoteNode) { - is TextNode -> { - withStyle( - style = SpanStyle( - fontSize = 12.sp, - color = MaterialTheme.colorScheme.secondary - ) - ) { - append(blockquoteNode.text()) - } - } - is Element -> { - when (blockquoteNode.tagName().lowercase()) { - "br" -> { - append('\n') - } - "a" -> { - append(blockquoteNode.ownText()) - } - } - } - } - } - } - HtmlText( - text = text, - modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp), - onClick = onTextClicked, - onLongClick = onTextLongClicked, - interactionSource = interactionSource - ) - } -} - -@Composable -private fun HtmlOrderedList( - orderedList: Element, - interactionSource: MutableInteractionSource, - modifier: Modifier = Modifier, - onTextClicked: () -> Unit = {}, - onTextLongClicked: () -> Unit = {}, -) { - val delimiter = "." - HtmlListItems( - list = orderedList, - marker = { index -> "$index$delimiter" }, - modifier = modifier, - onTextClicked = onTextClicked, onTextLongClicked = onTextLongClicked, - interactionSource = interactionSource - ) -} - -@Composable -private fun HtmlUnorderedList( - unorderedList: Element, - interactionSource: MutableInteractionSource, - modifier: Modifier = Modifier, - onTextClicked: () -> Unit = {}, - onTextLongClicked: () -> Unit = {}, -) { - val marker = "・" - HtmlListItems( - list = unorderedList, - marker = { marker }, - modifier = modifier, - onTextClicked = onTextClicked, onTextLongClicked = onTextLongClicked, - interactionSource = interactionSource - ) -} - -@Composable -private fun HtmlListItems( - list: Element, - marker: (Int) -> String, - interactionSource: MutableInteractionSource, - modifier: Modifier = Modifier, - onTextClicked: () -> Unit = {}, - onTextLongClicked: () -> Unit = {}, -) { - Column(modifier = modifier) { - for ((index, node) in list.children().withIndex()) { - val areAllChildrenInline = node.childNodes().all { it is TextNode || it is Element && it.isInline() } - if (areAllChildrenInline) { - val text = buildAnnotatedString { - append("${marker(index + 1)} ") - appendInlineChildrenElements(node.childNodes(), MaterialTheme.colorScheme) - } - HtmlText(text = text, interactionSource = remember { MutableInteractionSource() }) - } else { - for (innerNode in node.childNodes()) { - when (innerNode) { - is TextNode -> { - if (!innerNode.isBlank) { - val text = buildAnnotatedString { - append("${marker(index + 1)} ") - } - HtmlText( - text = text, onClick = onTextClicked, - onLongClick = onTextLongClicked, interactionSource = interactionSource - ) - } - } - is Element -> HtmlBlock( - element = innerNode, - modifier = Modifier.padding(start = 4.dp), - onTextClicked = onTextClicked, onTextLongClicked = onTextLongClicked, - interactionSource = interactionSource - ) - } - } - } - } - } -} - -private fun ColorScheme.codeBackground(): Color { - return background.copy(alpha = 0.3f) -} - -private fun AnnotatedString.Builder.appendInlineChildrenElements( - childNodes: List, - colors: ColorScheme -) { - for (node in childNodes) { - when (node) { - is TextNode -> { - append(node.text()) - } - is Element -> { - appendInlineElement(node, colors) - } - } - } -} - -private fun AnnotatedString.Builder.appendInlineElement(element: Element, colors: ColorScheme) { - when (element.tagName().lowercase()) { - "br" -> { - append('\n') - } - "code" -> { - withStyle( - style = TextStyle( - fontFamily = FontFamily.Monospace, - background = colors.codeBackground() - ).toSpanStyle() - ) { - appendInlineChildrenElements(element.childNodes(), colors) - } - } - "del" -> { - withStyle(style = SpanStyle(textDecoration = TextDecoration.LineThrough)) { - appendInlineChildrenElements(element.childNodes(), colors) - } - } - "i", - "em" -> { - withStyle(style = SpanStyle(fontStyle = FontStyle.Italic)) { - appendInlineChildrenElements(element.childNodes(), colors) - } - } - "strong" -> { - withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { - appendInlineChildrenElements(element.childNodes(), colors) - } - } - "a" -> { - appendLink(element) - } - else -> { - appendInlineChildrenElements(element.childNodes(), colors) - } - } -} - -private fun AnnotatedString.Builder.appendLink(link: Element) { - val uriString = link.attr("href") - val permalinkData = PermalinkParser.parse(uriString) - when (permalinkData) { - is PermalinkData.FallbackLink -> { - pushStringAnnotation(tag = "URL", annotation = permalinkData.uri.toString()) - withStyle( - style = SpanStyle(color = LinkColor) - ) { - append(link.ownText()) - } - pop() - } - is PermalinkData.RoomEmailInviteLink -> { - safeAppendInlineContent(CHIP_ID, link.ownText()) - } - is PermalinkData.RoomLink -> { - safeAppendInlineContent(CHIP_ID, link.ownText()) - } - is PermalinkData.UserLink -> { - safeAppendInlineContent(CHIP_ID, link.ownText()) - } - } -} - -fun AnnotatedString.Builder.safeAppendInlineContent(chipId: String, ownText: String) { - if (ownText.isEmpty()) { - // alternateText cannot be empty and default parameter value is private, - // so just omit the second param here. - appendInlineContent(chipId) - } else { - appendInlineContent(chipId, ownText) - } -} - -@Composable -private fun HtmlText( - text: AnnotatedString, - interactionSource: MutableInteractionSource, - modifier: Modifier = Modifier, - style: TextStyle = LocalTextStyle.current, - onClick: () -> Unit = {}, - onLongClick: () -> Unit = {}, -) { - val inlineContentMap = persistentMapOf() - ClickableLinkText( - annotatedString = text, - style = style, - modifier = modifier, - inlineContent = inlineContentMap, - interactionSource = interactionSource, - onClick = onClick, - onLongClick = onLongClick - ) -} - -@PreviewsDayNight -@Composable -internal fun HtmlDocumentPreview(@PreviewParameter(DocumentProvider::class) document: Document) = ElementPreview { - HtmlDocument( - document = document, - extraPadding = noExtraPadding, - interactionSource = remember { MutableInteractionSource() } - ) -} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt index e75e49c1e3..7c30350cc4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt @@ -17,7 +17,6 @@ package io.element.android.features.messages.impl.timeline.components.reactionsummary import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf @@ -38,10 +37,6 @@ class ReactionSummaryPresenter @Inject constructor( ) : Presenter { @Composable override fun present(): ReactionSummaryState { - LaunchedEffect(Unit) { - room.updateMembers() - } - val membersState by room.membersStateFlow.collectAsState() val target: MutableState = remember { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt index b9260e2768..3e6568d880 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt @@ -68,7 +68,7 @@ import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.model.getAvatarData -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateForTimelineItemEventRowProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateForTimelineItemEventRowProvider.kt new file mode 100644 index 0000000000..1595e59f54 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateForTimelineItemEventRowProvider.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components.receipt + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.messages.impl.timeline.model.ReadReceiptData +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState + +class ReadReceiptViewStateForTimelineItemEventRowProvider : + PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aReadReceiptViewState( + sendState = LocalEventSendState.NotSentYet + ), + aReadReceiptViewState( + sendState = LocalEventSendState.Sent(EventId("\$eventId")), + ), + aReadReceiptViewState( + sendState = LocalEventSendState.Sent(EventId("\$eventId")), + receipts = mutableListOf().apply { + repeat(5) { + add( + aReadReceiptData( + it + ) + ) + } + }, + ), + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt index e0f72b010d..a40e94c529 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt @@ -33,28 +33,28 @@ class ReadReceiptViewStateProvider : PreviewParameterProvider().apply { repeat(1) { add(aReadReceiptData(it)) } }, + receipts = List(1) { aReadReceiptData(it) }, ), aReadReceiptViewState( sendState = LocalEventSendState.Sent(EventId("\$eventId")), - receipts = mutableListOf().apply { repeat(2) { add(aReadReceiptData(it)) } }, + receipts = List(2) { aReadReceiptData(it) }, ), aReadReceiptViewState( sendState = LocalEventSendState.Sent(EventId("\$eventId")), - receipts = mutableListOf().apply { repeat(3) { add(aReadReceiptData(it)) } }, + receipts = List(3) { aReadReceiptData(it) }, ), aReadReceiptViewState( sendState = LocalEventSendState.Sent(EventId("\$eventId")), - receipts = mutableListOf().apply { repeat(4) { add(aReadReceiptData(it)) } }, + receipts = List(4) { aReadReceiptData(it) }, ), aReadReceiptViewState( sendState = LocalEventSendState.Sent(EventId("\$eventId")), - receipts = mutableListOf().apply { repeat(5) { add(aReadReceiptData(it)) } }, + receipts = List(5) { aReadReceiptData(it) }, ), ) } -private fun aReadReceiptViewState( +internal fun aReadReceiptViewState( sendState: LocalEventSendState? = null, isLastOutgoingMessage: Boolean = true, receipts: List = emptyList(), @@ -64,7 +64,7 @@ private fun aReadReceiptViewState( receipts = receipts.toImmutableList(), ) -private fun aReadReceiptData( +internal fun aReadReceiptData( index: Int, avatarData: AvatarData = anAvatarData( id = "$index", diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt index 8725b365d0..70e5486c2a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt @@ -35,7 +35,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.clearAndSetSemantics -import androidx.compose.ui.semantics.stateDescription +import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex @@ -50,7 +50,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonPlurals import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList @@ -69,7 +69,9 @@ fun TimelineItemReadReceiptView( receipts = state.receipts, modifier = Modifier .clip(RoundedCornerShape(4.dp)) - .clickable { onReadReceiptsClicked() } + .clickable { + onReadReceiptsClicked() + } .padding(2.dp) ) } @@ -80,7 +82,7 @@ fun TimelineItemReadReceiptView( Icon( modifier = Modifier.padding(2.dp), resourceId = CommonDrawables.ic_sending, - contentDescription = null, + contentDescription = stringResource(id = CommonStrings.common_sending), tint = ElementTheme.colors.iconSecondary ) } @@ -96,7 +98,7 @@ fun TimelineItemReadReceiptView( Icon( modifier = Modifier.padding(2.dp), resourceId = CommonDrawables.ic_sent, - contentDescription = null, + contentDescription = stringResource(id = CommonStrings.common_sent), tint = ElementTheme.colors.iconSecondary ) } @@ -139,7 +141,7 @@ private fun ReadReceiptsAvatars( Row( modifier = modifier .clearAndSetSemantics { - stateDescription = receiptDescription + contentDescription = receiptDescription }, horizontalArrangement = Arrangement.spacedBy(4.dp - avatarStrokeSize), verticalAlignment = Alignment.CenterVertically, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt index 391ec3de7f..be63fa3409 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt @@ -18,9 +18,10 @@ package io.element.android.features.messages.impl.timeline.components.receipt.bo import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable @@ -29,6 +30,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -38,7 +40,6 @@ import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.components.MatrixUserRow -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.launch @@ -83,34 +84,39 @@ internal fun ReadReceiptBottomSheet( } @Composable -private fun ColumnScope.ReadReceiptBottomSheetContent( +private fun ReadReceiptBottomSheetContent( state: ReadReceiptBottomSheetState, onUserDataClicked: (UserId) -> Unit, ) { - ListItem( - headlineContent = { - Text(text = stringResource(id = CommonStrings.common_seen_by)) + LazyColumn { + item { + ListItem( + headlineContent = { + Text(text = stringResource(id = CommonStrings.common_seen_by)) + } + ) + } + items( + items = state.selectedEvent?.readReceiptState?.receipts.orEmpty() + ) { + val userId = UserId(it.avatarData.id) + MatrixUserRow( + modifier = Modifier.clickable { onUserDataClicked(userId) }, + matrixUser = MatrixUser( + userId = userId, + displayName = it.avatarData.name, + avatarUrl = it.avatarData.url, + ), + avatarSize = AvatarSize.ReadReceiptList, + trailingContent = { + Text( + text = it.formattedDate, + style = ElementTheme.typography.fontBodySmRegular, + color = ElementTheme.colors.textSecondary, + ) + } + ) } - ) - val receipts = state.selectedEvent?.readReceiptState?.receipts.orEmpty() - receipts.forEach { - val userId = UserId(it.avatarData.id) - MatrixUserRow( - modifier = Modifier.clickable { onUserDataClicked(userId) }, - matrixUser = MatrixUser( - userId = userId, - displayName = it.avatarData.name, - avatarUrl = it.avatarData.url, - ), - avatarSize = AvatarSize.ReadReceiptList, - trailingContent = { - Text( - text = it.formattedDate, - style = ElementTheme.typography.fontBodySmRegular, - color = ElementTheme.colors.textSecondary, - ) - } - ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt index 6959c448cb..bbd35a7d4f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt @@ -38,7 +38,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.launch diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineEncryptedHistoryBannerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineEncryptedHistoryBannerView.kt index 241f98c141..1dda31ed82 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineEncryptedHistoryBannerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineEncryptedHistoryBannerView.kt @@ -31,14 +31,14 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.R import io.element.android.features.messages.impl.timeline.session.SessionState import io.element.android.features.messages.impl.timeline.session.SessionStateProvider import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme @Composable fun TimelineEncryptedHistoryBannerView( @@ -56,8 +56,8 @@ fun TimelineEncryptedHistoryBannerView( ) { Icon( modifier = Modifier.size(20.dp), - resourceId = CommonDrawables.ic_compound_info_solid, - contentDescription = "Info", + imageVector = CompoundIcons.InfoSolid, + contentDescription = null, tint = ElementTheme.colors.iconInfoPrimary ) Text( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt index bf69badb9e..f3b007354f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt @@ -31,7 +31,7 @@ import io.element.android.features.messages.impl.timeline.model.virtual.Timeline import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable internal fun TimelineItemDaySeparatorView( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemReadMarkerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemReadMarkerView.kt index ac58b14e60..0d8dca11b7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemReadMarkerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemReadMarkerView.kt @@ -30,7 +30,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable internal fun TimelineItemReadMarkerView( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt index f400c214a3..78ce6521d9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt @@ -31,7 +31,7 @@ import io.element.android.features.messages.impl.R import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable fun TimelineItemRoomBeginningView( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoView.kt index c93560100c..d3d05dc6a2 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoView.kt @@ -26,7 +26,6 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxWidth @@ -49,23 +48,23 @@ import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp import androidx.core.content.getSystemService +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.button.BackButton -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.core.EventId -import io.element.android.libraries.theme.ElementTheme /** * Screen used to display debug info for events. * It will only be available in debug builds. */ -@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable fun EventDebugInfoView( eventId: EventId?, @@ -140,7 +139,7 @@ private fun CollapsibleSection( Text(title, modifier = Modifier.weight(1f)) Icon( modifier = Modifier.rotate(if (isExpanded) 180f else 0f), - resourceId = CommonDrawables.ic_compound_chevron_down, + imageVector = CompoundIcons.ChevronDown, contentDescription = null ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt index b5a3c9545e..5e72392914 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt @@ -59,7 +59,7 @@ class TimelineItemContentFactory @Inject constructor( is RoomMembershipContent -> roomMembershipFactory.create(eventTimelineItem) is StateContent -> stateFactory.create(eventTimelineItem) is StickerContent -> stickerFactory.create(itemContent) - is PollContent -> pollFactory.create(itemContent, eventTimelineItem.eventId) + is PollContent -> pollFactory.create(eventTimelineItem, itemContent) is UnableToDecryptContent -> utdFactory.create(itemContent) is UnknownContent -> TimelineItemUnknownContent } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt index bf4ba27bd2..69c453bb44 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt @@ -16,7 +16,15 @@ package io.element.android.features.messages.impl.timeline.factories.event +import android.text.Spannable +import android.text.style.URLSpan +import android.text.util.Linkify +import androidx.core.text.buildSpannedString +import androidx.core.text.getSpans +import androidx.core.text.toSpannable +import androidx.core.text.util.LinkifyCompat import io.element.android.features.location.api.Location +import io.element.android.features.messages.api.timeline.HtmlConverterProvider import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEmoteContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent @@ -27,8 +35,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent -import io.element.android.features.messages.impl.timeline.util.FileExtensionExtractor -import io.element.android.features.messages.impl.timeline.util.toHtmlDocument import io.element.android.libraries.androidutils.filesize.FileSizeFormatter import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.featureflag.api.FeatureFlagService @@ -37,33 +43,41 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType import io.element.android.libraries.matrix.api.timeline.item.event.OtherMessageType import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType -import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType +import io.element.android.libraries.matrix.ui.messages.toHtmlDocument +import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractor import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -import java.time.Duration import javax.inject.Inject +import kotlin.time.Duration class TimelineItemContentMessageFactory @Inject constructor( private val fileSizeFormatter: FileSizeFormatter, private val fileExtensionExtractor: FileExtensionExtractor, private val featureFlagService: FeatureFlagService, + private val htmlConverterProvider: HtmlConverterProvider, ) { suspend fun create(content: MessageContent, senderDisplayName: String, eventId: EventId?): TimelineItemEventContent { return when (val messageType = content.type) { - is EmoteMessageType -> TimelineItemEmoteContent( - body = "* $senderDisplayName ${messageType.body}", - htmlDocument = messageType.formatted?.toHtmlDocument(prefix = "* $senderDisplayName"), - isEdited = content.isEdited, - ) + is EmoteMessageType -> { + val emoteBody = "* $senderDisplayName ${messageType.body}" + TimelineItemEmoteContent( + body = emoteBody, + htmlDocument = messageType.formatted?.toHtmlDocument(prefix = "* $senderDisplayName"), + formattedBody = parseHtml(messageType.formatted, prefix = "* $senderDisplayName") ?: emoteBody.withLinks(), + isEdited = content.isEdited, + ) + } is ImageMessageType -> { val aspectRatio = aspectRatioOf(messageType.info?.width, messageType.info?.height) TimelineItemImageContent( @@ -85,6 +99,8 @@ class TimelineItemContentMessageFactory @Inject constructor( TimelineItemTextContent( body = messageType.body, htmlDocument = null, + plainText = messageType.body, + formattedBody = null, isEdited = content.isEdited, ) } else { @@ -104,7 +120,7 @@ class TimelineItemContentMessageFactory @Inject constructor( mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, width = messageType.info?.width?.toInt(), height = messageType.info?.height?.toInt(), - duration = messageType.info?.duration?.toMillis() ?: 0L, + duration = messageType.info?.duration ?: Duration.ZERO, blurHash = messageType.info?.blurhash, aspectRatio = aspectRatio, formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0), @@ -159,23 +175,21 @@ class TimelineItemContentMessageFactory @Inject constructor( is NoticeMessageType -> TimelineItemNoticeContent( body = messageType.body, htmlDocument = messageType.formatted?.toHtmlDocument(), + formattedBody = parseHtml(messageType.formatted) ?: messageType.body.withLinks(), isEdited = content.isEdited, ) - is TextMessageType -> TimelineItemTextContent( - body = messageType.body, - htmlDocument = messageType.formatted?.toHtmlDocument(), - isEdited = content.isEdited, - ) + is TextMessageType -> { + TimelineItemTextContent( + body = messageType.body, + htmlDocument = messageType.formatted?.toHtmlDocument(), + formattedBody = parseHtml(messageType.formatted) ?: messageType.body.withLinks(), + isEdited = content.isEdited, + ) + } is OtherMessageType -> TimelineItemTextContent( body = messageType.body, htmlDocument = null, - isEdited = content.isEdited, - ) - UnknownMessageType -> TimelineItemTextContent( - // Display the body as a fallback, but should not happen anymore - // (we have `OtherMessageType` now) - body = content.body, - htmlDocument = null, + formattedBody = messageType.body.withLinks(), isEdited = content.isEdited, ) } @@ -190,4 +204,48 @@ class TimelineItemContentMessageFactory @Inject constructor( return result?.takeIf { it.isFinite() } } + + private fun parseHtml(formattedBody: FormattedBody?, prefix: String? = null): CharSequence? { + if (formattedBody == null || formattedBody.format != MessageFormat.HTML) return null + val result = htmlConverterProvider.provide() + .fromHtmlToSpans(formattedBody.body) + .withFixedURLSpans() + return if (prefix != null) { + buildSpannedString { + append(prefix) + append(" ") + append(result) + } + } else { + result + } + } + + private fun CharSequence.withFixedURLSpans(): CharSequence { + if (this !is Spannable) return this + // Get all URL spans, as they will be removed by LinkifyCompat.addLinks + val oldURLSpans = getSpans(0, length).associateWith { + val start = getSpanStart(it) + val end = getSpanEnd(it) + Pair(start, end) + } + // Find and set as URLSpans any links present in the text + LinkifyCompat.addLinks(this, Linkify.WEB_URLS or Linkify.PHONE_NUMBERS or Linkify.EMAIL_ADDRESSES) + // Restore old spans if they don't conflict with the new ones + for ((urlSpan, location) in oldURLSpans) { + val (start, end) = location + if (getSpans(start, end).isEmpty()) { + setSpan(urlSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + } + } + return this + } +} + +@Suppress("USELESS_ELVIS") +private fun String.withLinks(): CharSequence? { + /* Note: toSpannable() can return null when running unit tests */ + val spannable = toSpannable() ?: return null + val addedLinks = LinkifyCompat.addLinks(spannable, Linkify.WEB_URLS or Linkify.PHONE_NUMBERS or Linkify.EMAIL_ADDRESSES) + return spannable.takeIf { addedLinks } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollFactory.kt index 04551f7086..c3b417abd6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollFactory.kt @@ -19,63 +19,33 @@ package io.element.android.features.messages.impl.timeline.factories.event import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent -import io.element.android.features.poll.api.PollAnswerItem +import io.element.android.features.poll.api.pollcontent.PollContentStateFactory import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags -import io.element.android.libraries.matrix.api.MatrixClient -import io.element.android.libraries.matrix.api.core.EventId -import io.element.android.libraries.matrix.api.poll.isDisclosed +import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.PollContent import javax.inject.Inject class TimelineItemContentPollFactory @Inject constructor( - private val matrixClient: MatrixClient, private val featureFlagService: FeatureFlagService, + private val pollContentStateFactory: PollContentStateFactory, ) { suspend fun create( + event: EventTimelineItem, content: PollContent, - eventId: EventId? ): TimelineItemEventContent { if (!featureFlagService.isFeatureEnabled(FeatureFlags.Polls)) return TimelineItemUnknownContent - - // Todo Move this computation to the matrix rust sdk - val totalVoteCount = content.votes.flatMap { it.value }.size - val myVotes = content.votes.filter { matrixClient.sessionId in it.value }.keys - val isEndedPoll = content.endTime != null - val winnerIds = if (!isEndedPoll) { - emptyList() - } else { - content.answers - .map { answer -> answer.id } - .groupBy { answerId -> content.votes[answerId]?.size ?: 0 } // Group by votes count - .maxByOrNull { (votes, _) -> votes } // Keep max voted answers - ?.takeIf { (votes, _) -> votes > 0 } // Ignore if no option has been voted - ?.value - .orEmpty() - } - val answerItems = content.answers.map { answer -> - val answerVoteCount = content.votes[answer.id]?.size ?: 0 - val isSelected = answer.id in myVotes - val isWinner = answer.id in winnerIds - val percentage = if (totalVoteCount > 0) answerVoteCount.toFloat() / totalVoteCount.toFloat() else 0f - PollAnswerItem( - answer = answer, - isSelected = isSelected, - isEnabled = !isEndedPoll, - isWinner = isWinner, - isDisclosed = content.kind.isDisclosed || isEndedPoll, - votesCount = answerVoteCount, - percentage = percentage, - ) - } - + val pollContentState = pollContentStateFactory.create(event, content) return TimelineItemPollContent( - eventId = eventId, - question = content.question, - answerItems = answerItems, - pollKind = content.kind, - isEnded = isEndedPoll, + isMine = pollContentState.isMine, + isEditable = pollContentState.isPollEditable, + eventId = event.eventId, + question = pollContentState.question, + answerItems = pollContentState.answerItems, + pollKind = pollContentState.pollKind, + isEnded = pollContentState.isPollEnded, + isEdited = content.isEdited ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt index 4c9217a884..51520c66dc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt @@ -24,6 +24,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts +import io.element.android.features.messages.impl.timeline.model.map import io.element.android.libraries.core.bool.orTrue import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter import io.element.android.libraries.designsystem.components.avatar.AvatarData @@ -79,7 +80,7 @@ class TimelineItemEventFactory @Inject constructor( ) currentTimelineItem.event return TimelineItem.Event( - id = currentTimelineItem.uniqueId.toString(), + id = currentTimelineItem.uniqueId, eventId = currentTimelineItem.eventId, transactionId = currentTimelineItem.transactionId, senderId = currentSender, @@ -87,12 +88,13 @@ class TimelineItemEventFactory @Inject constructor( senderAvatar = senderAvatarData, content = contentFactory.create(currentTimelineItem.event), isMine = currentTimelineItem.event.isOwn, + isEditable = currentTimelineItem.event.isEditable, sentTime = sentTime, groupPosition = groupPosition, reactionsState = currentTimelineItem.computeReactionsState(), readReceiptState = currentTimelineItem.computeReadReceiptState(roomMembers), localSendState = currentTimelineItem.event.localSendState, - inReplyTo = currentTimelineItem.event.inReplyTo(), + inReplyTo = currentTimelineItem.event.inReplyTo()?.map(), isThreaded = currentTimelineItem.event.isThreaded(), debugInfo = currentTimelineItem.event.debugInfo, origin = currentTimelineItem.event.origin, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemVirtualFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemVirtualFactory.kt index 6178b1dee7..cc513705e3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemVirtualFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemVirtualFactory.kt @@ -31,13 +31,8 @@ class TimelineItemVirtualFactory @Inject constructor( fun create( virtualTimelineItem: MatrixTimelineItem.Virtual, ): TimelineItem.Virtual { - val id = if (virtualTimelineItem.virtual is VirtualTimelineItem.EncryptedHistoryBanner) { - "encrypted_history_banner" - } else { - virtualTimelineItem.uniqueId.toString() - } return TimelineItem.Virtual( - id = id, + id = virtualTimelineItem.uniqueId, model = virtualTimelineItem.computeModel() ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt index e9e9af6445..f819be4161 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt @@ -73,7 +73,8 @@ private fun MutableList.addGroup( add( TimelineItem.GroupedEvents( id = groupId, - events = groupOfItems.toImmutableList() + events = groupOfItems.toImmutableList(), + aggregatedReadReceipts = groupOfItems.flatMap { it.readReceiptState.receipts }.toImmutableList() ) ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToDetails.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToDetails.kt new file mode 100644 index 0000000000..14596ac1f0 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToDetails.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.model + +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.timeline.item.event.EventContent +import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo +import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType +import io.element.android.libraries.matrix.ui.messages.toPlainText + +data class InReplyToDetails( + val eventId: EventId, + val senderId: UserId, + val senderDisplayName: String?, + val senderAvatarUrl: String?, + val eventContent: EventContent?, + val textContent: String?, +) + +fun InReplyTo.map() = when (this) { + is InReplyTo.Ready -> InReplyToDetails( + eventId = eventId, + senderId = senderId, + senderDisplayName = senderDisplayName, + senderAvatarUrl = senderAvatarUrl, + eventContent = content, + textContent = when (content) { + is MessageContent -> { + val messageContent = content as MessageContent + (messageContent.type as? TextMessageType)?.toPlainText() ?: messageContent.body + } + else -> null + } + ) + else -> null +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToMetadata.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToMetadata.kt new file mode 100644 index 0000000000..63775d5ac2 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToMetadata.kt @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.model + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.ui.res.stringResource +import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType +import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo +import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType +import io.element.android.libraries.ui.strings.CommonStrings + +@Immutable +internal sealed interface InReplyToMetadata { + + val text: String? + + data class Thumbnail( + val attachmentThumbnailInfo: AttachmentThumbnailInfo + ) : InReplyToMetadata { + override val text: String? = attachmentThumbnailInfo.textContent + } + + data class Text( + override val text: String + ) : InReplyToMetadata +} + +/** + * Computes metadata for the in reply to message. + * + * Metadata can be either a thumbnail with a text OR just a text. + */ +@Composable +internal fun InReplyToDetails.metadata(): InReplyToMetadata? = when (eventContent) { + is MessageContent -> when (val type = eventContent.type) { + is ImageMessageType -> InReplyToMetadata.Thumbnail( + AttachmentThumbnailInfo( + thumbnailSource = type.info?.thumbnailSource ?: type.source, + textContent = eventContent.body, + type = AttachmentThumbnailType.Image, + blurHash = type.info?.blurhash, + ) + ) + is VideoMessageType -> InReplyToMetadata.Thumbnail( + AttachmentThumbnailInfo( + thumbnailSource = type.info?.thumbnailSource, + textContent = eventContent.body, + type = AttachmentThumbnailType.Video, + blurHash = type.info?.blurhash, + ) + ) + is FileMessageType -> InReplyToMetadata.Thumbnail( + AttachmentThumbnailInfo( + thumbnailSource = type.info?.thumbnailSource, + textContent = eventContent.body, + type = AttachmentThumbnailType.File, + ) + ) + is LocationMessageType -> InReplyToMetadata.Thumbnail( + AttachmentThumbnailInfo( + textContent = stringResource(CommonStrings.common_shared_location), + type = AttachmentThumbnailType.Location, + ) + ) + is AudioMessageType -> InReplyToMetadata.Thumbnail( + AttachmentThumbnailInfo( + textContent = eventContent.body, + type = AttachmentThumbnailType.Audio, + ) + ) + is VoiceMessageType -> InReplyToMetadata.Thumbnail( + AttachmentThumbnailInfo( + textContent = stringResource(CommonStrings.common_voice_message), + type = AttachmentThumbnailType.Voice, + ) + ) + else -> InReplyToMetadata.Text(textContent ?: eventContent.body) + } + is PollContent -> InReplyToMetadata.Thumbnail( + AttachmentThumbnailInfo( + textContent = eventContent.question, + type = AttachmentThumbnailType.Poll, + ) + ) + else -> null +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/NewEventState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/NewEventState.kt new file mode 100644 index 0000000000..9a00c9fd8f --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/NewEventState.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.model + +/** + * Model if there is a new event in the timeline and if it is from me or from other. + * This can be used to scroll to the bottom of the list when a new event is added. + */ +enum class NewEventState { + None, FromMe, FromOther +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt index 5ceaba5550..916a9b88b4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt @@ -25,7 +25,6 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo -import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin import kotlinx.collections.immutable.ImmutableList @@ -63,11 +62,12 @@ sealed interface TimelineItem { val content: TimelineItemEventContent, val sentTime: String = "", val isMine: Boolean = false, + val isEditable: Boolean, val groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.None, val reactionsState: TimelineItemReactions, val readReceiptState: TimelineItemReadReceipts, val localSendState: LocalEventSendState?, - val inReplyTo: InReplyTo?, + val inReplyTo: InReplyToDetails?, val isThreaded: Boolean, val debugInfo: TimelineItemDebugInfo, val origin: TimelineItemEventOrigin?, @@ -88,6 +88,6 @@ sealed interface TimelineItem { data class GroupedEvents( val id: String, val events: ImmutableList, + val aggregatedReadReceipts: ImmutableList, ) : TimelineItem - } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/BubbleState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/BubbleState.kt index 1912aac668..4f1fc3c546 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/BubbleState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/BubbleState.kt @@ -16,10 +16,12 @@ package io.element.android.features.messages.impl.timeline.model.bubble +import io.element.android.features.messages.impl.timeline.TimelineRoomInfo import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition data class BubbleState( val groupPosition: TimelineItemGroupPosition, val isMine: Boolean, val isHighlighted: Boolean, + val timelineRoomInfo: TimelineRoomInfo, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/BubbleStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/BubbleStateProvider.kt index fbcbcc5454..9c50a73a6b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/BubbleStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/BubbleStateProvider.kt @@ -17,6 +17,8 @@ package io.element.android.features.messages.impl.timeline.model.bubble import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.messages.impl.timeline.TimelineRoomInfo +import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition open class BubbleStateProvider : PreviewParameterProvider { @@ -29,7 +31,11 @@ open class BubbleStateProvider : PreviewParameterProvider { ).map { groupPosition -> sequenceOf(false, true).map { isMine -> sequenceOf(false, true).map { isHighlighted -> - BubbleState(groupPosition, isMine = isMine, isHighlighted = isHighlighted) + aBubbleState( + groupPosition = groupPosition, + isMine = isMine, + isHighlighted = isHighlighted, + ) } } .flatten() @@ -37,8 +43,14 @@ open class BubbleStateProvider : PreviewParameterProvider { .flatten() } -fun aBubbleState() = BubbleState( - groupPosition = TimelineItemGroupPosition.First, - isMine = false, - isHighlighted = false, +internal fun aBubbleState( + groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.First, + isMine: Boolean = false, + isHighlighted: Boolean = false, + timelineRoomInfo: TimelineRoomInfo = aTimelineRoomInfo(), +) = BubbleState( + groupPosition = groupPosition, + isMine = isMine, + isHighlighted = isHighlighted, + timelineRoomInfo = timelineRoomInfo, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt index 9d9a41e0e3..aa228efcbd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt @@ -16,9 +16,8 @@ package io.element.android.features.messages.impl.timeline.model.event -import io.element.android.features.messages.impl.media.helper.formatFileExtensionAndSize import io.element.android.libraries.matrix.api.media.MediaSource -import java.time.Duration +import kotlin.time.Duration data class TimelineItemAudioContent( val body: String, @@ -29,6 +28,10 @@ data class TimelineItemAudioContent( val fileExtension: String, ) : TimelineItemEventContent { - val fileExtensionAndSize = formatFileExtensionAndSize(fileExtension, formattedFileSize) + val fileExtensionAndSize = + io.element.android.libraries.mediaviewer.api.helper.formatFileExtensionAndSize( + fileExtension, + formattedFileSize + ) override val type: String = "TimelineItemAudioContent" } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContentProvider.kt index 06cb53b6fe..09c553730d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContentProvider.kt @@ -19,7 +19,7 @@ package io.element.android.features.messages.impl.timeline.model.event import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.media.MediaSource -import java.time.Duration +import kotlin.time.Duration.Companion.milliseconds open class TimelineItemAudioContentProvider : PreviewParameterProvider { override val values: Sequence @@ -35,6 +35,6 @@ fun aTimelineItemAudioContent(fileName: String = "A sound.mp3") = TimelineItemAu mimeType = MimeTypes.Pdf, formattedFileSize = "100kB", fileExtension = "mp3", - duration = Duration.ofMillis(100), + duration = 100.milliseconds, mediaSource = MediaSource(""), ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEmoteContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEmoteContent.kt index 27aa26ad80..37c02e4a90 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEmoteContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEmoteContent.kt @@ -16,11 +16,14 @@ package io.element.android.features.messages.impl.timeline.model.event +import io.element.android.libraries.matrix.ui.messages.toPlainText import org.jsoup.nodes.Document data class TimelineItemEmoteContent( override val body: String, override val htmlDocument: Document?, + override val plainText: String = htmlDocument?.toPlainText() ?: body, + override val formattedBody: CharSequence?, override val isEdited: Boolean, ) : TimelineItemTextBasedContent { override val type: String = "TimelineItemEmoteContent" diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt index 56c0b63b0e..feb3246e54 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt @@ -41,8 +41,7 @@ fun TimelineItemEventContent.canBeCopied(): Boolean = fun TimelineItemEventContent.canBeRepliedTo(): Boolean = when (this) { is TimelineItemRedactedContent, - is TimelineItemStateContent, - is TimelineItemPollContent -> false + is TimelineItemStateContent -> false else -> true } @@ -64,3 +63,13 @@ fun TimelineItemEventContent.canReact(): Boolean = is TimelineItemRedactedContent, TimelineItemUnknownContent -> false } + +/** + * Whether the event content has been edited. + */ +fun TimelineItemEventContent.isEdited(): Boolean = + when (this) { + is TimelineItemTextBasedContent -> isEdited + is TimelineItemPollContent -> isEdited + else -> false + } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentProvider.kt index bbbd8748a2..974607c33b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentProvider.kt @@ -16,9 +16,12 @@ package io.element.android.features.messages.impl.timeline.model.event +import android.graphics.Typeface +import android.text.style.StyleSpan import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.core.text.buildSpannedString +import androidx.core.text.inSpans import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent -import org.jsoup.Jsoup class TimelineItemEventContentProvider : PreviewParameterProvider { override val values = sequenceOf( @@ -43,19 +46,29 @@ class TimelineItemEventContentProvider : PreviewParameterProvider { + + private fun buildSpanned(text: String) = buildSpannedString { + inSpans(StyleSpan(Typeface.BOLD)) { + append("Rich Text") + } + append(" ") + append(text) + } + override val values = sequenceOf( aTimelineItemEmoteContent(), - aTimelineItemEmoteContent().copy(htmlDocument = Jsoup.parse("Emote Document")), + aTimelineItemEmoteContent().copy(formattedBody = buildSpanned("Emote")), aTimelineItemNoticeContent(), - aTimelineItemNoticeContent().copy(htmlDocument = Jsoup.parse("Notice Document")), + aTimelineItemNoticeContent().copy(formattedBody = buildSpanned("Notice")), aTimelineItemTextContent(), - aTimelineItemTextContent().copy(htmlDocument = Jsoup.parse("Text Document")), + aTimelineItemTextContent().copy(formattedBody = buildSpanned("Text")), ) } fun aTimelineItemEmoteContent() = TimelineItemEmoteContent( body = "Emote", htmlDocument = null, + formattedBody = null, isEdited = false, ) @@ -66,6 +79,7 @@ fun aTimelineItemEncryptedContent() = TimelineItemEncryptedContent( fun aTimelineItemNoticeContent() = TimelineItemNoticeContent( body = "Notice", htmlDocument = null, + formattedBody = null, isEdited = false, ) @@ -74,11 +88,14 @@ fun aTimelineItemRedactedContent() = TimelineItemRedactedContent fun aTimelineItemTextContent() = TimelineItemTextContent( body = "Text", htmlDocument = null, + formattedBody = null, isEdited = false, ) fun aTimelineItemUnknownContent() = TimelineItemUnknownContent -fun aTimelineItemStateEventContent() = TimelineItemStateEventContent( - body = "A state event", +fun aTimelineItemStateEventContent( + body: String = "A state event", +) = TimelineItemStateEventContent( + body = body, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemFileContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemFileContent.kt index aa35f5a117..04f651a8ad 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemFileContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemFileContent.kt @@ -16,8 +16,8 @@ package io.element.android.features.messages.impl.timeline.model.event -import io.element.android.features.messages.impl.media.helper.formatFileExtensionAndSize import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.mediaviewer.api.helper.formatFileExtensionAndSize data class TimelineItemFileContent( val body: String, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemLocationContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemLocationContentProvider.kt index 7ad79f19e8..dc5a03fa2b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemLocationContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemLocationContentProvider.kt @@ -18,10 +18,6 @@ package io.element.android.features.messages.impl.timeline.model.event import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.location.api.Location -import io.element.android.features.poll.api.PollAnswerItem -import io.element.android.libraries.matrix.api.core.EventId -import io.element.android.libraries.matrix.api.poll.PollAnswer -import io.element.android.libraries.matrix.api.poll.PollKind open class TimelineItemLocationContentProvider : PreviewParameterProvider { override val values: Sequence @@ -41,31 +37,3 @@ fun aTimelineItemLocationContent(description: String? = null) = TimelineItemLoca description = description, ) -fun aTimelineItemPollContent( - isEnded: Boolean = false, -) = TimelineItemPollContent( - eventId = EventId("\$anEventId"), - question = "Some question?", - answerItems = listOf( - PollAnswerItem( - answer = PollAnswer("id_1", "Answer1"), - isSelected = false, - isEnabled = false, - isWinner = false, - isDisclosed = false, - votesCount = 0, - percentage = 0.0f, - ), - PollAnswerItem( - answer = PollAnswer("id_2", "Answer2"), - isSelected = false, - isEnabled = false, - isWinner = false, - isDisclosed = false, - votesCount = 0, - percentage = 0.0f, - ), - ), - pollKind = PollKind.Disclosed, - isEnded = isEnded, -) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemNoticeContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemNoticeContent.kt index 175268093e..e9358370a3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemNoticeContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemNoticeContent.kt @@ -16,11 +16,14 @@ package io.element.android.features.messages.impl.timeline.model.event +import io.element.android.libraries.matrix.ui.messages.toPlainText import org.jsoup.nodes.Document data class TimelineItemNoticeContent( override val body: String, override val htmlDocument: Document?, + override val plainText: String = htmlDocument?.toPlainText() ?: body, + override val formattedBody: CharSequence?, override val isEdited: Boolean, ) : TimelineItemTextBasedContent { override val type: String = "TimelineItemNoticeContent" diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContent.kt index dc47c12a86..9a9c27c3d5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContent.kt @@ -16,16 +16,19 @@ package io.element.android.features.messages.impl.timeline.model.event -import io.element.android.features.poll.api.PollAnswerItem +import io.element.android.features.poll.api.pollcontent.PollAnswerItem import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.poll.PollKind data class TimelineItemPollContent( + val isMine: Boolean, + val isEditable: Boolean, val eventId: EventId?, val question: String, val answerItems: List, val pollKind: PollKind, val isEnded: Boolean, + val isEdited: Boolean ) : TimelineItemEventContent { override val type: String = "TimelineItemPollContent" } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentProvider.kt index c475f6dfbd..26f545fd5f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentProvider.kt @@ -17,7 +17,9 @@ package io.element.android.features.messages.impl.timeline.model.event import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.features.poll.api.aPollAnswerItemList +import io.element.android.features.poll.api.pollcontent.PollAnswerItem +import io.element.android.features.poll.api.pollcontent.aPollAnswerItemList +import io.element.android.features.poll.api.pollcontent.aPollQuestion import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.poll.PollKind @@ -26,15 +28,27 @@ open class TimelineItemPollContentProvider : PreviewParameterProvider = aPollAnswerItemList(), + isMine: Boolean = false, + isEditable: Boolean = false, + isEnded: Boolean = false, + isEdited: Boolean = false, +): TimelineItemPollContent { return TimelineItemPollContent( eventId = EventId("\$anEventId"), pollKind = PollKind.Disclosed, - question = "What type of food should we have at the party?", - answerItems = aPollAnswerItemList(), - isEnded = false, + question = question, + answerItems = answerItems, + isMine = isMine, + isEditable = isEditable, + isEnded = isEnded, + isEdited = isEdited, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemTextBasedContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemTextBasedContent.kt index 5d5f121b75..a0ece96855 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemTextBasedContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemTextBasedContent.kt @@ -23,6 +23,8 @@ import org.jsoup.nodes.Document sealed interface TimelineItemTextBasedContent : TimelineItemEventContent { val body: String val htmlDocument: Document? + val formattedBody: CharSequence? + val plainText: String val isEdited: Boolean val htmlBody: String? get() = htmlDocument?.body()?.html() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemTextContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemTextContent.kt index ecb50af31c..dc4728f300 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemTextContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemTextContent.kt @@ -16,12 +16,15 @@ package io.element.android.features.messages.impl.timeline.model.event +import io.element.android.libraries.matrix.ui.messages.toPlainText import org.jsoup.nodes.Document data class TimelineItemTextContent( override val body: String, override val htmlDocument: Document?, + override val plainText: String = htmlDocument?.toPlainText() ?: body, + override val formattedBody: CharSequence?, override val isEdited: Boolean, -) : TimelineItemTextBasedContent{ +) : TimelineItemTextBasedContent { override val type: String = "TimelineItemTextContent" } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContent.kt index 14ec0a972d..5519fbc7f1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContent.kt @@ -17,10 +17,11 @@ package io.element.android.features.messages.impl.timeline.model.event import io.element.android.libraries.matrix.api.media.MediaSource +import kotlin.time.Duration data class TimelineItemVideoContent( val body: String, - val duration: Long, + val duration: Duration, val videoSource: MediaSource, val thumbnailSource: MediaSource?, val aspectRatio: Float?, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContentProvider.kt index adff01bdc8..939636fadc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContentProvider.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.ui.components.A_BLUR_HASH +import kotlin.time.Duration.Companion.milliseconds open class TimelineItemVideoContentProvider : PreviewParameterProvider { override val values: Sequence @@ -35,7 +36,7 @@ fun aTimelineItemVideoContent() = TimelineItemVideoContent( thumbnailSource = null, blurHash = A_BLUR_HASH, aspectRatio = 0.5f, - duration = 100, + duration = 100.milliseconds, videoSource = MediaSource(""), height = 300, width = 150, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContent.kt index 0d3bb903de..395b5dd2b3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContent.kt @@ -19,7 +19,7 @@ package io.element.android.features.messages.impl.timeline.model.event import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.media.MediaSource import kotlinx.collections.immutable.ImmutableList -import java.time.Duration +import kotlin.time.Duration data class TimelineItemVoiceContent( val eventId: EventId?, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContentProvider.kt index 830255f06e..f27922a4dd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContentProvider.kt @@ -21,21 +21,23 @@ import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.media.MediaSource import kotlinx.collections.immutable.toPersistentList -import java.time.Duration +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.minutes open class TimelineItemVoiceContentProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aTimelineItemVoiceContent( - durationMs = 1, + duration = 1.milliseconds, waveform = listOf(), ), aTimelineItemVoiceContent( - durationMs = 10_000, + duration = 10_000.milliseconds, waveform = listOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f, 9f, 8f, 7f, 6f, 5f, 4f, 3f, 2f, 1f, 0f), ), aTimelineItemVoiceContent( - durationMs = 1_800_000, // 30 minutes + duration = 30.minutes, waveform = List(1024) { it / 1024f }, ), ) @@ -44,14 +46,14 @@ open class TimelineItemVoiceContentProvider : PreviewParameterProvider = listOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f, 9f, 8f, 7f, 6f, 5f, 4f, 3f, 2f, 1f, 0f), ) = TimelineItemVoiceContent( eventId = eventId?.let { EventId(it) }, body = body, - duration = Duration.ofMillis(durationMs), + duration = duration, mediaSource = MediaSource(contentUri), mimeType = mimeType, waveform = waveform.toPersistentList(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt index 6b3ed3d65e..762e43cf92 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt @@ -43,7 +43,7 @@ class MessageSummaryFormatterImpl @Inject constructor( ) : MessageSummaryFormatter { override fun format(event: TimelineItem.Event): String { return when (event.content) { - is TimelineItemTextBasedContent -> event.content.body + is TimelineItemTextBasedContent -> event.content.plainText is TimelineItemProfileChangeContent -> event.content.body is TimelineItemStateContent -> event.content.body is TimelineItemLocationContent -> context.getString(CommonStrings.common_shared_location) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPlayer.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPlayer.kt index 584ec96f2a..0e1b960ecc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPlayer.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPlayer.kt @@ -16,6 +16,7 @@ package io.element.android.features.messages.impl.voicemessages.composer +import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.mediaplayer.api.MediaPlayer import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -41,7 +42,7 @@ class VoiceMessageComposerPlayer @Inject constructor( private val coroutineScope: CoroutineScope, ) { companion object { - const val MIME_TYPE = "audio/ogg" + const val MIME_TYPE = MimeTypes.Ogg } private var mediaPath: String? = null @@ -201,6 +202,7 @@ class VoiceMessageComposerPlayer @Inject constructor( progress = 0f, ) } + /** * Whether this player is currently playing. */ @@ -212,7 +214,6 @@ class VoiceMessageComposerPlayer @Inject constructor( val isStopped get() = this.playState == PlayState.Stopped } - enum class PlayState { /** * The player is stopped, i.e. it has just been initialised. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt index cc8bd1945f..62ddc4845b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt @@ -24,6 +24,7 @@ import io.element.android.libraries.di.CacheDirectory import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrix.api.mxc.MxcTools import java.io.File /** @@ -66,6 +67,7 @@ interface VoiceMessageMediaRepo { class DefaultVoiceMessageMediaRepo @AssistedInject constructor( @CacheDirectory private val cacheDir: File, + mxcTools: MxcTools, private val matrixMediaLoader: MatrixMediaLoader, @Assisted private val mediaSource: MediaSource, @Assisted("mimeType") private val mimeType: String?, @@ -101,7 +103,7 @@ class DefaultVoiceMessageMediaRepo @AssistedInject constructor( } } - private val cachedFile: File? = mxcUri2FilePath(mediaSource.url)?.let { + private val cachedFile: File? = mxcTools.mxcUri2FilePath(mediaSource.url)?.let { File("${cacheDir.path}/$CACHE_VOICE_SUBDIR/$it") } } @@ -110,24 +112,3 @@ class DefaultVoiceMessageMediaRepo @AssistedInject constructor( * Subdirectory of the application's cache directory where voice messages are stored. */ private const val CACHE_VOICE_SUBDIR = "temp/voice" - -/** - * Regex to match a Matrix Content (mxc://) URI. - * - * See: https://spec.matrix.org/v1.8/client-server-api/#matrix-content-mxc-uris - */ -private val mxcRegex = Regex("""^mxc:\/\/([^\/]+)\/([^\/]+)$""") - -/** - * Sanitizes an mxcUri to be used as a relative file path. - * - * @param mxcUri the Matrix Content (mxc://) URI of the voice message. - * @return the relative file path as "/" or null if the mxcUri is invalid. - */ -private fun mxcUri2FilePath(mxcUri: String): String? = mxcRegex.matchEntire(mxcUri)?.let { match -> - buildString { - append(match.groupValues[1]) - append("/") - append(match.groupValues[2]) - } -} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt index a6f002c38a..76180397f4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt @@ -17,6 +17,7 @@ package io.element.android.features.messages.impl.voicemessages.timeline import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.media.MediaSource @@ -196,7 +197,7 @@ class DefaultVoiceMessagePlayer( mediaPlayer.setMedia( uri = mediaFile.path, mediaId = eventId.value, - mimeType = "audio/ogg", // Files in the voice cache have no extension so we need to set the mime type manually. + mimeType = MimeTypes.Ogg, // Files in the voice cache have no extension so we need to set the mime type manually. startPositionMs = if (state.isEnded) 0L else state.currentPosition, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt index aebed9dd57..fa6632308a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt @@ -98,7 +98,7 @@ class VoiceMessagePresenter @AssistedInject constructor( } } val duration by remember { - derivedStateOf { playerState.duration ?: content.duration.toMillis() } + derivedStateOf { playerState.duration ?: content.duration.inWholeMilliseconds } } val progress by remember { derivedStateOf { diff --git a/features/messages/impl/src/main/res/drawable/pause.xml b/features/messages/impl/src/main/res/drawable/pause.xml deleted file mode 100644 index 875a9ce403..0000000000 --- a/features/messages/impl/src/main/res/drawable/pause.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/features/messages/impl/src/main/res/drawable/play.xml b/features/messages/impl/src/main/res/drawable/play.xml deleted file mode 100644 index 4e9df7b71d..0000000000 --- a/features/messages/impl/src/main/res/drawable/play.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/features/messages/impl/src/main/res/values-cs/translations.xml b/features/messages/impl/src/main/res/values-cs/translations.xml index d8231ab1ee..62a5ceb29b 100644 --- a/features/messages/impl/src/main/res/values-cs/translations.xml +++ b/features/messages/impl/src/main/res/values-cs/translations.xml @@ -45,6 +45,7 @@ "Při načítání nastavení oznámení došlo k chybě." "Obnovení výchozího režimu se nezdařilo, zkuste to prosím znovu." "Nastavení režimu se nezdařilo, zkuste to prosím znovu." + "Váš domovský server tuto možnost nepodporuje v šifrovaných místnostech, v této místnosti nebudete dostávat upozornění." "Všechny zprávy" "V této místnosti mě upozornit na" "Zobrazit méně" diff --git a/features/messages/impl/src/main/res/values-de/translations.xml b/features/messages/impl/src/main/res/values-de/translations.xml index 16580c21c5..4798aaf696 100644 --- a/features/messages/impl/src/main/res/values-de/translations.xml +++ b/features/messages/impl/src/main/res/values-de/translations.xml @@ -17,6 +17,7 @@ "Dies ist der Anfang von %1$s." "Dies ist der Anfang dieses Gesprächs." "Neu" + "Den ganzen Raum benachrichtigen" "Prüfe, ob du alle aktuellen und zukünftigen Nachrichten dieses Benutzers ausblenden möchtest" "Kamera" "Foto machen" @@ -27,6 +28,7 @@ "Umfrage" "Textformatierung" "Der Nachrichtenverlauf ist derzeit in diesem Raum nicht verfügbar" + "Der Nachrichtenverlauf ist in diesem Raum nicht verfügbar. Verifiziere dieses Gerät, um deinen Nachrichtenverlauf zu sehen." "Benutzerdetails konnten nicht abgerufen werden" "Möchtest du sie wieder einladen?" "Du bist allein in diesem Chat" @@ -42,6 +44,7 @@ "Beim Laden der Benachrichtigungseinstellungen ist ein Fehler aufgetreten." "Fehler beim Wiederherstellen des Standardmodus. Bitte versuche es erneut." "Fehler beim Einstellen des Modus. Bitte versuche es erneut." + "Dein Homeserver unterstützt diese Option in verschlüsselten Räumen nicht. Du wirst in diesem Raum nicht benachrichtigt." "Alle Nachrichten" "Benachrichtige mich in diesem Raum bei" "Weniger anzeigen" @@ -50,6 +53,8 @@ "Deine Nachricht konnte nicht gesendet werden" "Emoji hinzufügen" "Weniger anzeigen" + "Zum Aufnehmen gedrückt halten" + "Alle" "Benutzer sperren" "Fehler beim Verarbeiten des hochgeladenen Mediums. Bitte versuche es erneut." "Nur Erwähnungen und Schlüsselwörter" diff --git a/features/messages/impl/src/main/res/values-fr/translations.xml b/features/messages/impl/src/main/res/values-fr/translations.xml index 2c9ab7574c..061cd73f41 100644 --- a/features/messages/impl/src/main/res/values-fr/translations.xml +++ b/features/messages/impl/src/main/res/values-fr/translations.xml @@ -44,6 +44,7 @@ "Une erreur s’est produite lors du chargement des paramètres de notification." "Échec de la restauration du mode par défaut, veuillez réessayer." "Échec de la configuration du mode, veuillez réessayer." + "Votre serveur d’accueil ne supporte pas cette option pour les salons chiffrés, vous ne serez pas notifié(e) dans ce salon." "Tous les messages" "Dans ce salon, prévenez-moi pour" "Afficher moins" diff --git a/features/messages/impl/src/main/res/values-hu/translations.xml b/features/messages/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..4917430141 --- /dev/null +++ b/features/messages/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,61 @@ + + + "Tevékenységek" + "Zászlók" + "Étel és ital" + "Állatok és természet" + "Tárgyak" + "Mosolyok és emberek" + "Utazás és helyek" + "Szimbólumok" + + "%1$d szobaváltozás" + "%1$d szobaváltozás" + + "Ez az üzenet jelentve lesz a Matrix-kiszolgáló rendszergazdájának. Nem fogja tudni elolvasni a titkosított üzeneteket." + "A tartalom jelentésének oka" + "Ez a(z) %1$s kezdete." + "Ez a beszélgetés kezdete." + "Új" + "Teljes szoba értesítése" + "Jelölje be, ha el akarja rejteni az összes jelenlegi és jövőbeli üzenetet ettől a felhasználótól" + "Kamera" + "Fénykép készítése" + "Videó rögzítése" + "Melléklet" + "Fénykép- és videótár" + "Hely" + "Szavazás" + "Szövegformázás" + "Az üzenetelőzmények jelenleg nem érhetők el." + "Az üzenetelőzmények nem érhetők el ebben a szobában. Ellenőrizze ezt az eszközt, hogy lássa az előzményeket." + "Nem sikerült letölteni a felhasználói adatokat" + "Visszahívja?" + "Egyedül van ebben a csevegésben" + "Üzenet másolva" + "Nincs jogosultsága arra, hogy bejegyzést tegyen közzé ebben a szobában" + "Egyéni beállítás engedélyezése" + "Ennek bekapcsolása felülírja az alapértelmezett beállítást" + "Értesítések kérése ebben a csevegésben ezekről:" + "Megváltoztathatja a %1$s." + "globális beállításokban" + "Alapértelmezett beállítás" + "Egyéni beállítás eltávolítása" + "Hiba történt az értesítési beállítások betöltésekor." + "Nem sikerült visszaállítani az alapértelmezett módot, próbálja újra." + "Nem sikerült a mód beállítása, próbálja újra." + "A Matrix-kiszolgálója nem támogatja ezt a beállítást a titkosított szobákban, egyes szobákban nem fog értesítéseket kapni." + "Összes üzenet" + "Ebben a szobában, értesítés ezekről:" + "Kevesebb megjelenítése" + "Több megjelenítése" + "Újraküldés" + "Az üzenet elküldése sikertelen" + "Emodzsi hozzáadása" + "Kevesebb megjelenítése" + "Tartsa a rögzítéshez" + "Mindenki" + "Felhasználó letiltása" + "Nem sikerült feldolgozni a feltöltendő médiát, próbálja újra." + "Csak említések és kulcsszavak" + diff --git a/features/messages/impl/src/main/res/values-in/translations.xml b/features/messages/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..3e792da48f --- /dev/null +++ b/features/messages/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,60 @@ + + + "Aktivitas" + "Bendera" + "Makanan & Minuman" + "Hewan & Alam" + "Objek" + "Senyuman & Orang" + "Wisata & Tempat" + "Simbol" + + "%1$d perubahan ruangan" + + "Pesan ini akan dilaporkan ke administrator homeserver Anda. Mereka tidak akan dapat membaca pesan terenkripsi apa pun." + "Alasan melaporkan konten ini" + "Ini adalah awal dari %1$s." + "Ini adalah awal dari percakapan ini." + "Baru" + "Beri tahu seluruh ruangan" + "Centang jika Anda ingin menyembunyikan semua pesan saat ini dan yang akan datang dari pengguna ini" + "Kamera" + "Ambil foto" + "Rekam video" + "Lampiran" + "Pustaka Foto & Video" + "Lokasi" + "Pemungutan suara" + "Pemformatan Teks" + "Riwayat pesan saat ini tidak tersedia di ruangan ini" + "Riwayat pesan tidak tersedia di ruangan ini. Verifikasi perangkat ini untuk melihat riwayat pesan." + "Tidak dapat mengambil detail pengguna" + "Apakah Anda ingin mengundang mereka kembali?" + "Anda sendirian di obrolan ini" + "Pesan disalin" + "Anda tidak memiliki izin untuk mengirim di ruangan ini" + "Izinkan pengaturan khusus" + "Mengaktifkan ini akan mengganti pengaturan bawaan Anda" + "Beri tahu saya di obrolan ini tentang" + "Anda dapat mengubahnya di %1$s Anda." + "pengaturan global" + "Pengaturan bawaan" + "Hapus pengaturan khusus" + "Terjadi kesalahan saat memuat pengaturan pemberitahuan." + "Gagal memulihkan mode bawaan, silakan coba lagi." + "Gagal mengatur mode, silakan coba lagi." + "Homeserver Anda tidak mendukung opsi ini dalam ruangan terenkripsi, Anda tidak akan diberi tahu dalam ruangan ini." + "Semua pesan" + "Di ruangan ini, beri tahu saya tentang" + "Tampilkan lebih sedikit" + "Tampilkan lebih banyak" + "Kirim ulang" + "Pesan Anda gagal dikirim" + "Tambahkan emoji" + "Tampilkan lebih sedikit" + "Tahan untuk merekam" + "Semua orang" + "Blokir pengguna" + "Gagal memproses media untuk diunggah, silakan coba lagi." + "Sebutan dan Kata Kunci saja" + diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/FakeMessagesNavigator.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt similarity index 87% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/FakeMessagesNavigator.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt index bb2caa9405..de2b2e5bf5 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/FakeMessagesNavigator.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt @@ -14,9 +14,8 @@ * limitations under the License. */ -package io.element.android.features.messages +package io.element.android.features.messages.impl -import io.element.android.features.messages.impl.MessagesNavigator import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo @@ -31,6 +30,9 @@ class FakeMessagesNavigator : MessagesNavigator { var onReportContentClickedCount = 0 private set + var onEditPollClickedCount = 0 + private set + override fun onShowEventDebugInfoClicked(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { onShowEventDebugInfoClickedCount++ } @@ -42,4 +44,8 @@ class FakeMessagesNavigator : MessagesNavigator { override fun onReportContentClicked(eventId: EventId, senderId: UserId) { onReportContentClickedCount++ } + + override fun onEditPollClicked(eventId: EventId) { + onEditPollClickedCount++ + } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt similarity index 86% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt index 7fdb2885c4..876884b6cf 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt @@ -14,26 +14,26 @@ * limitations under the License. */ -package io.element.android.features.messages +package io.element.android.features.messages.impl import android.net.Uri import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow +import app.cash.turbine.ReceiveTurbine import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import im.vector.app.features.analytics.plan.PollEnd -import io.element.android.features.messages.fixtures.aMessageEvent -import io.element.android.features.messages.fixtures.aTimelineItemsFactory -import io.element.android.features.messages.impl.InviteDialogAction -import io.element.android.features.messages.impl.MessagesEvents -import io.element.android.features.messages.impl.MessagesPresenter import io.element.android.features.messages.impl.actionlist.ActionListPresenter import io.element.android.features.messages.impl.actionlist.ActionListState import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction +import io.element.android.features.messages.impl.fixtures.aMessageEvent +import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactory import io.element.android.features.messages.impl.messagecomposer.MessageComposerContextImpl import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter +import io.element.android.features.messages.impl.messagesummary.FakeMessageSummaryFormatter +import io.element.android.features.messages.impl.textcomposer.TestRichTextEditorStateFactory import io.element.android.features.messages.impl.timeline.TimelinePresenter import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionPresenter +import io.element.android.features.messages.impl.timeline.components.customreaction.FakeEmojibaseProvider import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryPresenter import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetPresenter import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuPresenter @@ -41,15 +41,15 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPlayer import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPresenter -import io.element.android.features.messages.media.FakeLocalMediaFactory +import io.element.android.features.messages.impl.voicemessages.timeline.FakeRedactedVoiceMessageManager import io.element.android.features.messages.test.FakeMessageComposerContext -import io.element.android.features.messages.textcomposer.TestRichTextEditorStateFactory -import io.element.android.features.messages.timeline.components.customreaction.FakeEmojibaseProvider -import io.element.android.features.messages.utils.messagesummary.FakeMessageSummaryFormatter -import io.element.android.features.messages.voicemessages.timeline.FakeRedactedVoiceMessageManager +import io.element.android.features.messages.test.timeline.FakeHtmlConverterProvider import io.element.android.features.networkmonitor.test.FakeNetworkMonitor +import io.element.android.features.poll.test.actions.FakeEndPollAction +import io.element.android.features.poll.test.actions.FakeSendPollResponseAction import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper import io.element.android.libraries.architecture.Async import io.element.android.libraries.core.coroutine.CoroutineDispatchers @@ -82,6 +82,7 @@ import io.element.android.libraries.mediapickers.test.FakePickerProvider import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer import io.element.android.libraries.mediaupload.api.MediaSender import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor +import io.element.android.libraries.mediaviewer.test.FakeLocalMediaFactory import io.element.android.libraries.permissions.api.PermissionsPresenter import io.element.android.libraries.permissions.test.FakePermissionsPresenter import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory @@ -92,14 +93,15 @@ import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.consumeItemsUntilPredicate import io.element.android.tests.testutils.consumeItemsUntilTimeout import io.element.android.tests.testutils.testCoroutineDispatchers -import io.element.android.tests.testutils.waitForPredicate import io.mockk.mockk +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test import kotlin.time.Duration.Companion.milliseconds +@Suppress("LargeClass") class MessagesPresenterTest { @get:Rule @@ -127,6 +129,21 @@ class MessagesPresenterTest { } } + @Test + fun `present - call is disabled if user cannot join it even if there is an ongoing call`() = runTest { + val room = FakeMatrixRoom().apply { + givenCanUserJoinCall(Result.success(false)) + givenRoomInfo(aRoomInfo(hasRoomCall = true)) + } + val presenter = createMessagesPresenter(matrixRoom = room) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = consumeItemsUntilTimeout().last() + assertThat(initialState.callState).isEqualTo(RoomCallState.DISABLED) + } + } + @Test fun `present - handle toggling a reaction`() = runTest { val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true) @@ -155,8 +172,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink.invoke(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID)) assertThat(room.myReactions.count()).isEqualTo(1) @@ -172,6 +188,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Forward, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -187,8 +204,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Copy, event)) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) assertThat(clipboardHelper.clipboardContents).isEqualTo((event.content as TimelineItemTextContent).body) @@ -201,8 +217,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, aMessageEvent())) val finalState = awaitItem() assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java) @@ -216,6 +231,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(3) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, aMessageEvent(eventId = null))) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -230,8 +246,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() val mediaMessage = aMessageEvent( content = TimelineItemImageContent( body = "image.jpg", @@ -261,12 +276,11 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() val mediaMessage = aMessageEvent( content = TimelineItemVideoContent( body = "video.mp4", - duration = 10L, + duration = 10.milliseconds, videoSource = MediaSource(AN_AVATAR_URL), thumbnailSource = MediaSource(AN_AVATAR_URL), mimeType = MimeTypes.Mp4, @@ -293,8 +307,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() val mediaMessage = aMessageEvent( content = TimelineItemFileContent( body = "file.pdf", @@ -320,8 +333,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Edit, aMessageEvent())) val finalState = awaitItem() assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Edit::class.java) @@ -329,6 +341,19 @@ class MessagesPresenterTest { } } + @Test + fun `present - handle action edit poll`() = runTest { + val navigator = FakeMessagesNavigator() + val presenter = createMessagesPresenter(navigator = navigator) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitFirstItem() + initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Edit, aMessageEvent(content = aTimelineItemPollContent()))) + assertThat(navigator.onEditPollClickedCount).isEqualTo(1) + } + } + @Test fun `present - handle action redact`() = runTest { val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true) @@ -353,6 +378,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.ReportContent, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -366,10 +392,10 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.Dismiss) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) - } } @@ -380,6 +406,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.ViewSource, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -420,8 +447,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.showReinvitePrompt).isFalse() initialState.composerState.richTextEditorState.requestFocus() val focusedState = awaitItem() @@ -436,8 +462,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.showReinvitePrompt).isFalse() initialState.composerState.richTextEditorState.requestFocus() val focusedState = awaitItem() @@ -450,7 +475,7 @@ class MessagesPresenterTest { val room = FakeMatrixRoom(sessionId = A_SESSION_ID) room.givenRoomMembersState( MatrixRoomMembersState.Ready( - listOf( + persistentListOf( aRoomMember(userId = A_SESSION_ID, membership = RoomMembershipState.JOIN), aRoomMember(userId = A_SESSION_ID_2, membership = RoomMembershipState.LEAVE), ) @@ -477,7 +502,7 @@ class MessagesPresenterTest { room.givenRoomMembersState( MatrixRoomMembersState.Error( failure = Throwable(), - prevRoomMembers = listOf( + prevRoomMembers = persistentListOf( aRoomMember(userId = A_SESSION_ID, membership = RoomMembershipState.JOIN), aRoomMember(userId = A_SESSION_ID_2, membership = RoomMembershipState.LEAVE), ) @@ -523,7 +548,7 @@ class MessagesPresenterTest { val room = FakeMatrixRoom(sessionId = A_SESSION_ID) room.givenRoomMembersState( MatrixRoomMembersState.Ready( - listOf( + persistentListOf( aRoomMember(userId = A_SESSION_ID, membership = RoomMembershipState.JOIN), aRoomMember(userId = A_SESSION_ID_2, membership = RoomMembershipState.LEAVE), ) @@ -555,8 +580,8 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - assertThat(awaitItem().userHasPermissionToSendMessage).isTrue() + val state = awaitFirstItem() + assertThat(state.userHasPermissionToSendMessage).isTrue() } } @@ -590,28 +615,32 @@ class MessagesPresenterTest { } @Test - fun `present - handle poll end`() = runTest { - val room = FakeMatrixRoom() - val analyticsService = FakeAnalyticsService() - val presenter = createMessagesPresenter( - matrixRoom = room, - analyticsService = analyticsService, - ) + fun `present - handle action reply to a poll`() = runTest { + val presenter = createMessagesPresenter() moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() - initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.EndPoll, aMessageEvent())) - waitForPredicate { room.endPollInvocations.size == 1 } - cancelAndIgnoreRemainingEvents() - assertThat(room.endPollInvocations.size).isEqualTo(1) - assertThat(room.endPollInvocations.first().pollStartId).isEqualTo(AN_EVENT_ID) - assertThat(room.endPollInvocations.first().text).isEqualTo("The poll with event id: \$anEventId has ended.") - assertThat(analyticsService.capturedEvents.size).isEqualTo(1) - assertThat(analyticsService.capturedEvents.last()).isEqualTo(PollEnd()) + val initialState = awaitFirstItem() + val poll = aMessageEvent( + content = aTimelineItemPollContent() + ) + initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, poll)) + val finalState = awaitItem() + assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java) + val replyMode = finalState.composerState.mode as MessageComposerMode.Reply + assertThat(replyMode.attachmentThumbnailInfo).isNotNull() + assertThat(replyMode.attachmentThumbnailInfo?.textContent) + .isEqualTo("What type of food should we have at the party?") + assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) } } + private suspend fun ReceiveTurbine.awaitFirstItem(): T { + // Skip 2 item if Mentions feature is enabled, else 1 + skipItems(if (FeatureFlags.Mentions.defaultValue) 2 else 1) + return awaitItem() + } + private fun TestScope.createMessagesPresenter( coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(), matrixRoom: MatrixRoom = FakeMatrixRoom().apply { @@ -621,6 +650,7 @@ class MessagesPresenterTest { clipboardHelper: FakeClipboardHelper = FakeClipboardHelper(), analyticsService: FakeAnalyticsService = FakeAnalyticsService(), permissionsPresenter: PermissionsPresenter = FakePermissionsPresenter(), + currentSessionIdHolder: CurrentSessionIdHolder = CurrentSessionIdHolder(FakeMatrixClient(A_SESSION_ID)), ): MessagesPresenter { val mediaSender = MediaSender(FakeMediaPreProcessor(), matrixRoom) val permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionsPresenter) @@ -652,12 +682,19 @@ class MessagesPresenterTest { room = matrixRoom, dispatchers = coroutineDispatchers, appScope = this, - analyticsService = analyticsService, + navigator = navigator, encryptionService = FakeEncryptionService(), verificationService = FakeSessionVerificationService(), featureFlagService = FakeFeatureFlagService(), redactedVoiceMessageManager = FakeRedactedVoiceMessageManager(), + endPollAction = FakeEndPollAction(), + sendPollResponseAction = FakeSendPollResponseAction(), ) + val timelinePresenterFactory = object : TimelinePresenter.Factory { + override fun create(navigator: MessagesNavigator): TimelinePresenter { + return timelinePresenter + } + } val preferencesStore = InMemoryPreferencesStore(isRichTextEditorEnabled = true) val actionListPresenter = ActionListPresenter(preferencesStore = preferencesStore) val readReceiptBottomSheetPresenter = ReadReceiptBottomSheetPresenter() @@ -668,7 +705,7 @@ class MessagesPresenterTest { room = matrixRoom, composerPresenter = messageComposerPresenter, voiceMessageComposerPresenter = voiceMessageComposerPresenter, - timelinePresenter = timelinePresenter, + timelinePresenterFactory = timelinePresenterFactory, actionListPresenter = actionListPresenter, customReactionPresenter = customReactionPresenter, reactionSummaryPresenter = reactionSummaryPresenter, @@ -683,6 +720,8 @@ class MessagesPresenterTest { featureFlagsService = FakeFeatureFlagService(), buildMeta = aBuildMeta(), dispatchers = coroutineDispatchers, + currentSessionIdHolder = currentSessionIdHolder, + htmlConverterProvider = FakeHtmlConverterProvider(), ) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt similarity index 90% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt index 460979f289..3c8dcf6e85 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt @@ -14,17 +14,14 @@ * limitations under the License. */ -package io.element.android.features.messages.actionlist +package io.element.android.features.messages.impl.actionlist import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import io.element.android.features.messages.fixtures.aMessageEvent -import io.element.android.features.messages.impl.actionlist.ActionListEvents -import io.element.android.features.messages.impl.actionlist.ActionListPresenter -import io.element.android.features.messages.impl.actionlist.ActionListState import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction +import io.element.android.features.messages.impl.fixtures.aMessageEvent import io.element.android.features.messages.impl.timeline.aTimelineItemEvent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent @@ -32,6 +29,7 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemVoiceContent +import io.element.android.features.poll.api.pollcontent.aPollAnswerItemList import io.element.android.libraries.featureflag.test.InMemoryPreferencesStore import io.element.android.libraries.matrix.test.A_MESSAGE import io.element.android.tests.testutils.WarmUpRule @@ -115,7 +113,7 @@ class ActionListPresenterTest { val initialState = awaitItem() val messageEvent = aMessageEvent( isMine = false, - content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false) + content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null) ) initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true)) // val loadingState = awaitItem() @@ -147,7 +145,7 @@ class ActionListPresenterTest { val initialState = awaitItem() val messageEvent = aMessageEvent( isMine = false, - content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false) + content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null) ) initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = false)) // val loadingState = awaitItem() @@ -178,7 +176,7 @@ class ActionListPresenterTest { val initialState = awaitItem() val messageEvent = aMessageEvent( isMine = false, - content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false) + content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null) ) initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = true, canSendMessage = true)) val successState = awaitItem() @@ -209,7 +207,7 @@ class ActionListPresenterTest { val initialState = awaitItem() val messageEvent = aMessageEvent( isMine = true, - content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false) + content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null) ) initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true)) // val loadingState = awaitItem() @@ -330,7 +328,7 @@ class ActionListPresenterTest { val initialState = awaitItem() val messageEvent = aMessageEvent( isMine = true, - content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false) + content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null) ) initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true)) // val loadingState = awaitItem() @@ -362,7 +360,7 @@ class ActionListPresenterTest { val initialState = awaitItem() val messageEvent = aMessageEvent( isMine = true, - content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false) + content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null) ) val redactedEvent = aMessageEvent( isMine = true, @@ -390,7 +388,7 @@ class ActionListPresenterTest { val messageEvent = aMessageEvent( eventId = null, // No event id, so it's not sent yet isMine = true, - content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false), + content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null), ) initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true)) @@ -410,7 +408,7 @@ class ActionListPresenterTest { } @Test - fun `present - compute for poll message`() = runTest { + fun `present - compute for editable poll message`() = runTest { val presenter = createActionListPresenter(isDeveloperModeEnabled = false) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -418,7 +416,8 @@ class ActionListPresenterTest { val initialState = awaitItem() val messageEvent = aMessageEvent( isMine = true, - content = aTimelineItemPollContent(), + isEditable = true, + content = aTimelineItemPollContent(answerItems = aPollAnswerItemList(hasVotes = false)), ) initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true)) val successState = awaitItem() @@ -426,6 +425,35 @@ class ActionListPresenterTest { ActionListState.Target.Success( messageEvent, persistentListOf( + TimelineItemAction.Reply, + TimelineItemAction.Edit, + TimelineItemAction.EndPoll, + TimelineItemAction.Redact, + ) + ) + ) + assertThat(successState.displayEmojiReactions).isTrue() + } + } + @Test + fun `present - compute for non-editable poll message`() = runTest { + val presenter = createActionListPresenter(isDeveloperModeEnabled = false) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + val messageEvent = aMessageEvent( + isMine = true, + isEditable = false, + content = aTimelineItemPollContent(answerItems = aPollAnswerItemList(hasVotes = true)), + ) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true)) + val successState = awaitItem() + assertThat(successState.target).isEqualTo( + ActionListState.Target.Success( + messageEvent, + persistentListOf( + TimelineItemAction.Reply, TimelineItemAction.EndPoll, TimelineItemAction.Redact, ) @@ -444,6 +472,7 @@ class ActionListPresenterTest { val initialState = awaitItem() val messageEvent = aMessageEvent( isMine = true, + isEditable = false, content = aTimelineItemPollContent(isEnded = true), ) initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true)) @@ -452,6 +481,7 @@ class ActionListPresenterTest { ActionListState.Target.Success( messageEvent, persistentListOf( + TimelineItemAction.Reply, TimelineItemAction.Redact, ) ) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt similarity index 95% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt index 22f28f21d0..5b8afcfe37 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt @@ -16,24 +16,23 @@ @file:OptIn(ExperimentalCoroutinesApi::class) -package io.element.android.features.messages.attachments +package io.element.android.features.messages.impl.attachments import android.net.Uri import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import io.element.android.features.messages.fixtures.aLocalMedia -import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewEvents import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewPresenter import io.element.android.features.messages.impl.attachments.preview.SendActionState -import io.element.android.features.messages.impl.media.local.LocalMedia import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.mediaupload.api.MediaPreProcessor import io.element.android.libraries.mediaupload.api.MediaSender import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia import io.element.android.tests.testutils.WarmUpRule import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/aMediaAttachment.kt similarity index 66% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/aMediaAttachment.kt index 00701c0a75..fb6eabfee1 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/aMediaAttachment.kt @@ -14,24 +14,12 @@ * limitations under the License. */ -package io.element.android.features.messages.fixtures +package io.element.android.features.messages.impl.fixtures -import android.net.Uri import io.element.android.features.messages.impl.attachments.Attachment -import io.element.android.features.messages.impl.media.local.LocalMedia -import io.element.android.features.messages.impl.media.local.MediaInfo -import io.element.android.features.messages.impl.media.local.anImageInfo - -fun aLocalMedia( - uri: Uri, - mediaInfo: MediaInfo = anImageInfo(), -) = LocalMedia( - uri = uri, - info = mediaInfo -) +import io.element.android.libraries.mediaviewer.api.local.LocalMedia fun aMediaAttachment(localMedia: LocalMedia, compressIfPossible: Boolean = true) = Attachment.Media( localMedia = localMedia, compressIfPossible = compressIfPossible, ) - diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/aMessageEvent.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/aMessageEvent.kt similarity index 87% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/aMessageEvent.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/aMessageEvent.kt index 68c80d6b80..f700ed34c3 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/aMessageEvent.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/aMessageEvent.kt @@ -14,9 +14,11 @@ * limitations under the License. */ -package io.element.android.features.messages.fixtures +package io.element.android.features.messages.impl.fixtures +import io.element.android.features.messages.impl.timeline.aTimelineItemDebugInfo import io.element.android.features.messages.impl.timeline.aTimelineItemReactions +import io.element.android.features.messages.impl.timeline.model.InReplyToDetails import io.element.android.features.messages.impl.timeline.model.ReadReceiptData import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts @@ -27,19 +29,18 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState -import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_MESSAGE import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_NAME -import io.element.android.libraries.matrix.test.room.aTimelineItemDebugInfo import kotlinx.collections.immutable.toImmutableList internal fun aMessageEvent( eventId: EventId? = AN_EVENT_ID, isMine: Boolean = true, - content: TimelineItemEventContent = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false), - inReplyTo: InReplyTo? = null, + isEditable: Boolean = true, + content: TimelineItemEventContent = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, formattedBody = null, isEdited = false), + inReplyTo: InReplyToDetails? = null, isThreaded: Boolean = false, debugInfo: TimelineItemDebugInfo = aTimelineItemDebugInfo(), sendState: LocalEventSendState = LocalEventSendState.Sent(AN_EVENT_ID), @@ -52,6 +53,7 @@ internal fun aMessageEvent( content = content, sentTime = "", isMine = isMine, + isEditable = isEditable, reactionsState = aTimelineItemReactions(count = 0), readReceiptState = TimelineItemReadReceipts(emptyList().toImmutableList()), localSendState = sendState, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/timelineItemsFactory.kt similarity index 90% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/timelineItemsFactory.kt index 972f105a4a..587d9f47df 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/timelineItemsFactory.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.fixtures +package io.element.android.features.messages.impl.fixtures import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentFactory @@ -32,7 +32,8 @@ import io.element.android.features.messages.impl.timeline.factories.event.Timeli import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemDaySeparatorFactory import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemVirtualFactory import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper -import io.element.android.features.messages.impl.timeline.util.FileExtensionExtractorWithoutValidation +import io.element.android.features.messages.test.timeline.FakeHtmlConverterProvider +import io.element.android.features.poll.test.pollcontent.FakePollContentStateFactory import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter import io.element.android.libraries.dateformatter.test.FakeDaySeparatorFormatter import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter @@ -40,6 +41,7 @@ import io.element.android.libraries.eventformatter.api.TimelineEventFormatter import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorWithoutValidation import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.test.TestScope @@ -54,10 +56,11 @@ internal fun TestScope.aTimelineItemsFactory(): TimelineItemsFactory { fileSizeFormatter = FakeFileSizeFormatter(), fileExtensionExtractor = FileExtensionExtractorWithoutValidation(), featureFlagService = FakeFeatureFlagService(), + htmlConverterProvider = FakeHtmlConverterProvider(), ), redactedMessageFactory = TimelineItemContentRedactedFactory(), stickerFactory = TimelineItemContentStickerFactory(), - pollFactory = TimelineItemContentPollFactory(matrixClient, FakeFeatureFlagService()), + pollFactory = TimelineItemContentPollFactory(FakeFeatureFlagService(), FakePollContentStateFactory()), utdFactory = TimelineItemContentUTDFactory(), roomMembershipFactory = TimelineItemContentRoomMembershipFactory(timelineEventFormatter), profileChangeFactory = TimelineItemContentProfileChangeFactory(timelineEventFormatter), diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenterTests.kt new file mode 100644 index 0000000000..1e35ec94bd --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenterTests.kt @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.forward + +import app.cash.molecule.RecompositionMode +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.aRoomSummaryDetail +import io.element.android.tests.testutils.WarmUpRule +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test + +class ForwardMessagesPresenterTests { + + @get:Rule + val warmUpRule = WarmUpRule() + + @Test + fun `present - initial state`() = runTest { + val presenter = aPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.isForwarding).isFalse() + assertThat(initialState.error).isNull() + assertThat(initialState.forwardingSucceeded).isNull() + } + } + + @Test + fun `present - forward successful`() = runTest { + val presenter = aPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + skipItems(1) + val summary = aRoomSummaryDetail() + presenter.onRoomSelected(listOf(summary.roomId)) + val forwardingState = awaitItem() + assertThat(forwardingState.isForwarding).isTrue() + val successfulForwardState = awaitItem() + assertThat(successfulForwardState.isForwarding).isFalse() + assertThat(successfulForwardState.forwardingSucceeded).isNotNull() + } + } + + @Test + fun `present - select a room and forward failed, then clear`() = runTest { + val room = FakeMatrixRoom() + val presenter = aPresenter(fakeMatrixRoom = room) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + // Test failed forwarding + room.givenForwardEventResult(Result.failure(Throwable("error"))) + skipItems(1) + val summary = aRoomSummaryDetail() + presenter.onRoomSelected(listOf(summary.roomId)) + skipItems(1) + val failedForwardState = awaitItem() + assertThat(failedForwardState.error).isNotNull() + // Then clear error + failedForwardState.eventSink(ForwardMessagesEvents.ClearError) + assertThat(awaitItem().error).isNull() + } + } + + private fun CoroutineScope.aPresenter( + eventId: EventId = AN_EVENT_ID, + fakeMatrixRoom: FakeMatrixRoom = FakeMatrixRoom(), + coroutineScope: CoroutineScope = this, + ) = ForwardMessagesPresenter( + eventId = eventId.value, + room = fakeMatrixRoom, + matrixCoroutineScope = coroutineScope, + ) +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/utils/messagesummary/FakeMessageSummaryFormatter.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagesummary/FakeMessageSummaryFormatter.kt similarity index 93% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/utils/messagesummary/FakeMessageSummaryFormatter.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagesummary/FakeMessageSummaryFormatter.kt index 3f8205e555..16f262c965 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/utils/messagesummary/FakeMessageSummaryFormatter.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagesummary/FakeMessageSummaryFormatter.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.utils.messagesummary +package io.element.android.features.messages.impl.messagesummary import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.utils.messagesummary.MessageSummaryFormatter diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/report/ReportMessagePresenterTests.kt similarity index 96% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/report/ReportMessagePresenterTests.kt index 94b971f300..d09feaf49f 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/report/ReportMessagePresenterTests.kt @@ -14,14 +14,12 @@ * limitations under the License. */ -package io.element.android.features.messages.report +package io.element.android.features.messages.impl.report import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import io.element.android.features.messages.impl.report.ReportMessageEvents -import io.element.android.features.messages.impl.report.ReportMessagePresenter import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.matrix.api.room.MatrixRoom diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenterTest.kt similarity index 93% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenterTest.kt index a48f55d120..295dc71a58 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenterTest.kt @@ -16,7 +16,7 @@ @file:OptIn(ExperimentalCoroutinesApi::class) -package io.element.android.features.messages.textcomposer +package io.element.android.features.messages.impl.textcomposer import android.net.Uri import androidx.compose.runtime.remember @@ -32,7 +32,6 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter import io.element.android.features.messages.impl.messagecomposer.MessageComposerState -import io.element.android.features.messages.media.FakeLocalMediaFactory import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.featureflag.api.FeatureFlagService @@ -67,6 +66,7 @@ import io.element.android.libraries.mediaupload.api.MediaPreProcessor import io.element.android.libraries.mediaupload.api.MediaSender import io.element.android.libraries.mediaupload.api.MediaUploadInfo import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor +import io.element.android.libraries.mediaviewer.test.FakeLocalMediaFactory import io.element.android.libraries.permissions.api.PermissionsPresenter import io.element.android.libraries.permissions.test.FakePermissionsPresenter import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory @@ -78,11 +78,11 @@ import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.waitForPredicate import io.mockk.mockk +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest -import okhttp3.internal.immutableListOf import org.junit.Rule import org.junit.Test import uniffi.wysiwyg_composer.MentionsState @@ -112,8 +112,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.isFullScreen).isFalse() assertThat(initialState.richTextEditorState.messageHtml).isEqualTo("") assertThat(initialState.mode).isEqualTo(MessageComposerMode.Normal) @@ -129,8 +128,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink.invoke(MessageComposerEvents.ToggleFullScreenState) val fullscreenState = awaitItem() assertThat(fullscreenState.isFullScreen).isTrue() @@ -146,8 +144,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.richTextEditorState.setHtml(A_MESSAGE) assertThat(initialState.richTextEditorState.messageHtml).isEqualTo(A_MESSAGE) initialState.richTextEditorState.setHtml("") @@ -162,8 +159,7 @@ class MessageComposerPresenterTest { val state = presenter.present() remember(state, state.richTextEditorState.messageHtml) { state } }.test { - skipItems(1) - var state = awaitItem() + var state = awaitFirstItem() val mode = anEditMode() state.eventSink.invoke(MessageComposerEvents.SetMode(mode)) state = awaitItem() @@ -183,8 +179,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - var state = awaitItem() + var state = awaitFirstItem() val mode = aReplyMode() state.eventSink.invoke(MessageComposerEvents.SetMode(mode)) state = awaitItem() @@ -200,8 +195,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - var state = awaitItem() + var state = awaitFirstItem() val mode = aReplyMode() state.eventSink.invoke(MessageComposerEvents.SetMode(mode)) state = awaitItem() @@ -220,8 +214,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - var state = awaitItem() + var state = awaitFirstItem() val mode = aQuoteMode() state.eventSink.invoke(MessageComposerEvents.SetMode(mode)) state = awaitItem() @@ -238,8 +231,7 @@ class MessageComposerPresenterTest { val state = presenter.present() remember(state, state.richTextEditorState.messageHtml) { state } }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.richTextEditorState.setHtml(A_MESSAGE) val withMessageState = awaitItem() assertThat(withMessageState.richTextEditorState.messageHtml).isEqualTo(A_MESSAGE) @@ -269,8 +261,7 @@ class MessageComposerPresenterTest { val state = presenter.present() remember(state, state.richTextEditorState.messageHtml) { state } }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.richTextEditorState.messageHtml).isEqualTo("") val mode = anEditMode() initialState.eventSink.invoke(MessageComposerEvents.SetMode(mode)) @@ -308,8 +299,7 @@ class MessageComposerPresenterTest { val state = presenter.present() remember(state, state.richTextEditorState.messageHtml) { state } }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.richTextEditorState.messageHtml).isEqualTo("") val mode = anEditMode(eventId = null, transactionId = A_TRANSACTION_ID) initialState.eventSink.invoke(MessageComposerEvents.SetMode(mode)) @@ -346,8 +336,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.richTextEditorState.messageHtml).isEqualTo("") val mode = aReplyMode() initialState.eventSink.invoke(MessageComposerEvents.SetMode(mode)) @@ -377,11 +366,10 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() - assertThat(initialState.showAttachmentSourcePicker).isEqualTo(false) + val initialState = awaitFirstItem() + assertThat(initialState.showAttachmentSourcePicker).isFalse() initialState.eventSink(MessageComposerEvents.AddAttachment) - assertThat(awaitItem().showAttachmentSourcePicker).isEqualTo(true) + assertThat(awaitItem().showAttachmentSourcePicker).isTrue() } } @@ -391,8 +379,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.AddAttachment) skipItems(1) @@ -426,8 +413,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) val previewingState = awaitItem() assertThat(previewingState.showAttachmentSourcePicker).isFalse() @@ -461,8 +447,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) val previewingState = awaitItem() assertThat(previewingState.showAttachmentSourcePicker).isFalse() @@ -480,8 +465,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) // No crashes here, otherwise it fails } @@ -501,8 +485,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles) val sendingState = awaitItem() assertThat(sendingState.showAttachmentSourcePicker).isFalse() @@ -523,8 +506,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.AddAttachment) val attachmentOpenState = awaitItem() assertThat(attachmentOpenState.showAttachmentSourcePicker).isTrue() @@ -541,8 +523,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.AddAttachment) val attachmentOpenState = awaitItem() assertThat(attachmentOpenState.showAttachmentSourcePicker).isTrue() @@ -564,8 +545,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.PhotoFromCamera) val finalState = awaitItem() assertThat(finalState.attachmentsState).isInstanceOf(AttachmentsState.Previewing::class.java) @@ -585,8 +565,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.PhotoFromCamera) val permissionState = awaitItem() assertThat(permissionState.showAttachmentSourcePicker).isFalse() @@ -611,8 +590,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.VideoFromCamera) val finalState = awaitItem() assertThat(finalState.attachmentsState).isInstanceOf(AttachmentsState.Previewing::class.java) @@ -632,8 +610,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.VideoFromCamera) val permissionState = awaitItem() assertThat(permissionState.showAttachmentSourcePicker).isFalse() @@ -655,8 +632,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles) val sendingState = awaitItem() assertThat(sendingState.attachmentsState).isInstanceOf(AttachmentsState.Sending::class.java) @@ -675,8 +651,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles) val sendingState = awaitItem() assertThat(sendingState.showAttachmentSourcePicker).isFalse() @@ -693,8 +668,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.Error(testException)) assertThat(analyticsService.trackedErrors).containsExactly(testException) } @@ -706,8 +680,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.showTextFormatting).isFalse() initialState.eventSink(MessageComposerEvents.AddAttachment) val composerOptions = awaitItem() @@ -733,9 +706,11 @@ class MessageComposerPresenterTest { isDirect = false, isOneToOne = false, ).apply { - givenRoomMembersState(MatrixRoomMembersState.Ready( - immutableListOf(currentUser, invitedUser, bob, david), - )) + givenRoomMembersState( + MatrixRoomMembersState.Ready( + persistentListOf(currentUser, invitedUser, bob, david), + ) + ) givenCanTriggerRoomNotification(Result.success(true)) } val flagsService = FakeFeatureFlagService( @@ -797,9 +772,11 @@ class MessageComposerPresenterTest { isDirect = true, isOneToOne = true, ).apply { - givenRoomMembersState(MatrixRoomMembersState.Ready( - immutableListOf(currentUser, invitedUser, bob, david), - )) + givenRoomMembersState( + MatrixRoomMembersState.Ready( + persistentListOf(currentUser, invitedUser, bob, david), + ) + ) givenCanTriggerRoomNotification(Result.success(true)) } val flagsService = FakeFeatureFlagService( @@ -828,8 +805,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.richTextEditorState.setHtml("Hey @bo") initialState.eventSink(MessageComposerEvents.InsertMention(MentionSuggestion.Member(aRoomMember(userId = A_USER_ID_2)))) @@ -846,8 +822,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() // Check intentional mentions on message sent val mentionUser1 = listOf(A_USER_ID.value) @@ -862,7 +837,7 @@ class MessageComposerPresenterTest { advanceUntilIdle() - assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID.value))) + assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID))) // Check intentional mentions on reply sent initialState.eventSink(MessageComposerEvents.SetMode(aReplyMode())) @@ -877,7 +852,7 @@ class MessageComposerPresenterTest { initialState.eventSink(MessageComposerEvents.SendMessage(A_MESSAGE.toMessage())) advanceUntilIdle() - assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID_2.value))) + assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID_2))) // Check intentional mentions on edit message skipItems(1) @@ -893,7 +868,7 @@ class MessageComposerPresenterTest { initialState.eventSink(MessageComposerEvents.SendMessage(A_MESSAGE.toMessage())) advanceUntilIdle() - assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID_3.value))) + assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID_3))) skipItems(1) } @@ -929,6 +904,12 @@ class MessageComposerPresenterTest { currentSessionIdHolder = CurrentSessionIdHolder(FakeMatrixClient(A_SESSION_ID)), permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionPresenter), ) + + private suspend fun ReceiveTurbine.awaitFirstItem(): T { + // Skip 2 item if Mentions feature is enabled, else 1 + skipItems(if (FeatureFlags.Mentions.defaultValue) 2 else 1) + return awaitItem() + } } fun anEditMode( diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/TestRichTextEditorStateFactory.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/textcomposer/TestRichTextEditorStateFactory.kt similarity index 94% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/TestRichTextEditorStateFactory.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/textcomposer/TestRichTextEditorStateFactory.kt index 762d144cd6..17e1c65daf 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/TestRichTextEditorStateFactory.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/textcomposer/TestRichTextEditorStateFactory.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.textcomposer +package io.element.android.features.messages.impl.textcomposer import androidx.compose.runtime.Composable import io.element.android.features.messages.impl.messagecomposer.RichTextEditorStateFactory diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/DefaultHtmlConverterProviderTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/DefaultHtmlConverterProviderTest.kt new file mode 100644 index 0000000000..0bfe497a0f --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/DefaultHtmlConverterProviderTest.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline + +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.test.junit4.createComposeRule +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.test.A_USER_ID +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class DefaultHtmlConverterProviderTest { + + @get:Rule val composeTestRule = createComposeRule() + + @Test + fun `calling provide without calling Update first should throw an exception`() { + val provider = DefaultHtmlConverterProvider() + + val exception = runCatching { provider.provide() }.exceptionOrNull() + + assertThat(exception).isInstanceOf(IllegalStateException::class.java) + } + + @Test + fun `calling provide after calling Update first should return an HtmlConverter`() { + val provider = DefaultHtmlConverterProvider() + composeTestRule.setContent { + CompositionLocalProvider(LocalInspectionMode provides true) { + provider.Update(currentUserId = A_USER_ID) + } + } + + val htmlConverter = runCatching { provider.provide() }.getOrNull() + + assertThat(htmlConverter).isNotNull() + } +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt similarity index 66% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt index 15ec930d2e..8bb3c0dcb8 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt @@ -14,26 +14,28 @@ * limitations under the License. */ -package io.element.android.features.messages.timeline +package io.element.android.features.messages.impl.timeline import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow +import app.cash.turbine.ReceiveTurbine import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import im.vector.app.features.analytics.plan.PollEnd -import im.vector.app.features.analytics.plan.PollVote -import io.element.android.features.messages.fixtures.aMessageEvent -import io.element.android.features.messages.fixtures.aTimelineItemsFactory -import io.element.android.features.messages.impl.timeline.TimelineEvents -import io.element.android.features.messages.impl.timeline.TimelinePresenter +import io.element.android.features.messages.impl.FakeMessagesNavigator +import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactory import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory +import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.session.SessionState -import io.element.android.libraries.featureflag.test.FakeFeatureFlagService +import io.element.android.features.messages.impl.voicemessages.timeline.FakeRedactedVoiceMessageManager import io.element.android.features.messages.impl.voicemessages.timeline.RedactedVoiceMessageManager -import io.element.android.features.messages.voicemessages.timeline.FakeRedactedVoiceMessageManager -import io.element.android.features.messages.voicemessages.timeline.aRedactedMatrixTimeline -import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.features.messages.impl.voicemessages.timeline.aRedactedMatrixTimeline +import io.element.android.features.poll.api.actions.EndPollAction +import io.element.android.features.poll.api.actions.SendPollResponseAction +import io.element.android.features.poll.test.actions.FakeEndPollAction +import io.element.android.features.poll.test.actions.FakeSendPollResponseAction +import io.element.android.libraries.featureflag.api.FeatureFlags +import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction @@ -42,16 +44,17 @@ import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTime import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService import io.element.android.libraries.matrix.test.room.FakeMatrixRoom -import io.element.android.libraries.matrix.test.room.aMessageContent -import io.element.android.libraries.matrix.test.room.anEventTimelineItem import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline +import io.element.android.libraries.matrix.test.timeline.aMessageContent +import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService import io.element.android.libraries.matrix.ui.components.aMatrixUserList -import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.awaitLastSequentialItem import io.element.android.tests.testutils.awaitWithLatch +import io.element.android.tests.testutils.consumeItemsUntilPredicate import io.element.android.tests.testutils.testCoroutineDispatchers -import io.element.android.tests.testutils.waitForPredicate +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.delay import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest @@ -59,6 +62,8 @@ import org.junit.Rule import org.junit.Test import java.util.Date +private const val FAKE_UNIQUE_ID = "FAKE_UNIQUE_ID" + class TimelinePresenterTest { @get:Rule @@ -70,7 +75,7 @@ class TimelinePresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.timelineItems).isEmpty() val loadedNoTimelineState = awaitItem() assertThat(loadedNoTimelineState.timelineItems).isEmpty() @@ -84,7 +89,7 @@ class TimelinePresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.paginationState.hasMoreToLoadBackwards).isTrue() assertThat(initialState.paginationState.isBackPaginating).isFalse() initialState.eventSink.invoke(TimelineEvents.LoadMore) @@ -103,7 +108,7 @@ class TimelinePresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() + val initialState = awaitFirstItem() skipItems(1) assertThat(initialState.highlightedEventId).isNull() initialState.eventSink.invoke(TimelineEvents.SetHighlightedEvent(AN_EVENT_ID)) @@ -119,7 +124,7 @@ class TimelinePresenterTest { fun `present - on scroll finished send read receipt if an event is before the index`() = runTest { val timeline = FakeMatrixTimeline( initialTimelineItems = listOf( - MatrixTimelineItem.Event(0, anEventTimelineItem()) + MatrixTimelineItem.Event(FAKE_UNIQUE_ID, anEventTimelineItem()) ) ) val presenter = createTimelinePresenter(timeline) @@ -127,7 +132,7 @@ class TimelinePresenterTest { presenter.present() }.test { assertThat(timeline.sendReadReceiptCount).isEqualTo(0) - val initialState = awaitItem() + val initialState = awaitFirstItem() // Wait for timeline items to be populated skipItems(1) awaitWithLatch { latch -> @@ -143,7 +148,7 @@ class TimelinePresenterTest { fun `present - on scroll finished will not send read receipt if no event is before the index`() = runTest { val timeline = FakeMatrixTimeline( initialTimelineItems = listOf( - MatrixTimelineItem.Event(0, anEventTimelineItem()) + MatrixTimelineItem.Event(FAKE_UNIQUE_ID, anEventTimelineItem()) ) ) val presenter = createTimelinePresenter(timeline) @@ -151,7 +156,7 @@ class TimelinePresenterTest { presenter.present() }.test { assertThat(timeline.sendReadReceiptCount).isEqualTo(0) - val initialState = awaitItem() + val initialState = awaitFirstItem() // Wait for timeline items to be populated skipItems(1) awaitWithLatch { latch -> @@ -167,7 +172,7 @@ class TimelinePresenterTest { fun `present - on scroll finished will not send read receipt only virtual events exist before the index`() = runTest { val timeline = FakeMatrixTimeline( initialTimelineItems = listOf( - MatrixTimelineItem.Virtual(0, VirtualTimelineItem.ReadMarker) + MatrixTimelineItem.Virtual(FAKE_UNIQUE_ID, VirtualTimelineItem.ReadMarker) ) ) val presenter = createTimelinePresenter(timeline) @@ -175,7 +180,7 @@ class TimelinePresenterTest { presenter.present() }.test { assertThat(timeline.sendReadReceiptCount).isEqualTo(0) - val initialState = awaitItem() + val initialState = awaitFirstItem() // Wait for timeline items to be populated skipItems(1) awaitWithLatch { latch -> @@ -188,28 +193,49 @@ class TimelinePresenterTest { } @Test - fun `present - covers hasNewItems scenarios`() = runTest { + fun `present - covers newEventState scenarios`() = runTest { val timeline = FakeMatrixTimeline() val presenter = createTimelinePresenter(timeline) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() - assertThat(initialState.hasNewItems).isFalse() + val initialState = awaitFirstItem() + assertThat(initialState.newEventState).isEqualTo(NewEventState.None) assertThat(initialState.timelineItems.size).isEqualTo(0) timeline.updateTimelineItems { - listOf(MatrixTimelineItem.Event(0, anEventTimelineItem(content = aMessageContent()))) + listOf(MatrixTimelineItem.Event("0", anEventTimelineItem(content = aMessageContent()))) } - skipItems(1) - assertThat(awaitItem().timelineItems.size).isEqualTo(1) + consumeItemsUntilPredicate { it.timelineItems.size == 1 } + // Mimics sending a message, and assert newEventState is FromMe timeline.updateTimelineItems { items -> - items + listOf(MatrixTimelineItem.Event(1, anEventTimelineItem(content = aMessageContent()))) + val event = anEventTimelineItem(content = aMessageContent(), isOwn = true) + items + listOf(MatrixTimelineItem.Event("1", event)) } - skipItems(1) - assertThat(awaitItem().timelineItems.size).isEqualTo(2) - assertThat(awaitItem().hasNewItems).isTrue() + consumeItemsUntilPredicate { it.timelineItems.size == 2 } + awaitLastSequentialItem().also { state -> + assertThat(state.newEventState).isEqualTo(NewEventState.FromMe) + } + // Mimics receiving a message without clearing the previous FromMe + timeline.updateTimelineItems { items -> + val event = anEventTimelineItem(content = aMessageContent()) + items + listOf(MatrixTimelineItem.Event("2", event)) + } + consumeItemsUntilPredicate { it.timelineItems.size == 3 } + + // Scroll to bottom to clear previous FromMe initialState.eventSink.invoke(TimelineEvents.OnScrollFinished(0)) - assertThat(awaitItem().hasNewItems).isFalse() + awaitLastSequentialItem().also { state -> + assertThat(state.newEventState).isEqualTo(NewEventState.None) + } + // Mimics receiving a message and assert newEventState is FromOther + timeline.updateTimelineItems { items -> + val event = anEventTimelineItem(content = aMessageContent()) + items + listOf(MatrixTimelineItem.Event("3", event)) + } + consumeItemsUntilPredicate { it.timelineItems.size == 4 } + awaitLastSequentialItem().also { state -> + assertThat(state.newEventState).isEqualTo(NewEventState.FromOther) + } cancelAndIgnoreRemainingEvents() } } @@ -221,31 +247,31 @@ class TimelinePresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() - assertThat(initialState.hasNewItems).isFalse() + val initialState = awaitFirstItem() + assertThat(initialState.newEventState).isEqualTo(NewEventState.None) assertThat(initialState.timelineItems.size).isEqualTo(0) val now = Date().time - val minuteInMilis = 60 * 1000 + val minuteInMillis = 60 * 1000 // Use index as a convenient value for timestamp val (alice, bob, charlie) = aMatrixUserList().take(3).mapIndexed { i, user -> - ReactionSender(senderId = user.userId, timestamp = now + i * minuteInMilis) + ReactionSender(senderId = user.userId, timestamp = now + i * minuteInMillis) } - val oneReaction = listOf( + val oneReaction = persistentListOf( EventReaction( key = "❤️", - senders = listOf(alice, charlie) + senders = persistentListOf(alice, charlie) ), EventReaction( key = "👍", - senders = listOf(alice, bob) + senders = persistentListOf(alice, bob) ), EventReaction( key = "🐶", - senders = listOf(charlie) + senders = persistentListOf(charlie) ), ) timeline.updateTimelineItems { - listOf(MatrixTimelineItem.Event(0, anEventTimelineItem(reactions = oneReaction))) + listOf(MatrixTimelineItem.Event(FAKE_UNIQUE_ID, anEventTimelineItem(reactions = oneReaction))) } skipItems(1) val item = awaitItem().timelineItems.first() @@ -270,47 +296,48 @@ class TimelinePresenterTest { } @Test - fun `present - PollAnswerSelected event calls into rust room api and analytics`() = runTest { - val room = FakeMatrixRoom() - val analyticsService = FakeAnalyticsService() + fun `present - PollAnswerSelected event`() = runTest { + val sendPollResponseAction = FakeSendPollResponseAction() val presenter = createTimelinePresenter( - room = room, - analyticsService = analyticsService, + sendPollResponseAction = sendPollResponseAction, ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink.invoke(TimelineEvents.PollAnswerSelected(AN_EVENT_ID, "anAnswerId")) } delay(1) - assertThat(room.sendPollResponseInvocations.size).isEqualTo(1) - assertThat(room.sendPollResponseInvocations.first().answers).isEqualTo(listOf("anAnswerId")) - assertThat(room.sendPollResponseInvocations.first().pollStartId).isEqualTo(AN_EVENT_ID) - assertThat(analyticsService.capturedEvents.size).isEqualTo(1) - assertThat(analyticsService.capturedEvents.last()).isEqualTo(PollVote()) + sendPollResponseAction.verifyExecutionCount(1) } @Test - fun `present - PollEndClicked event calls into rust room api and analytics`() = runTest { - val room = FakeMatrixRoom() - val analyticsService = FakeAnalyticsService() + fun `present - PollEndClicked event`() = runTest { + val endPollAction = FakeEndPollAction() val presenter = createTimelinePresenter( - room = room, - analyticsService = analyticsService, + endPollAction = endPollAction, ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() - initialState.eventSink(TimelineEvents.PollEndClicked(aMessageEvent().eventId!!)) - waitForPredicate { room.endPollInvocations.size == 1 } - cancelAndIgnoreRemainingEvents() - assertThat(room.endPollInvocations.size).isEqualTo(1) - assertThat(room.endPollInvocations.first().pollStartId).isEqualTo(AN_EVENT_ID) - assertThat(room.endPollInvocations.first().text).isEqualTo("The poll with event id: \$anEventId has ended.") - assertThat(analyticsService.capturedEvents.size).isEqualTo(1) - assertThat(analyticsService.capturedEvents.last()).isEqualTo(PollEnd()) + val initialState = awaitFirstItem() + initialState.eventSink.invoke(TimelineEvents.PollEndClicked(AN_EVENT_ID)) + } + delay(1) + endPollAction.verifyExecutionCount(1) + } + + @Test + fun `present - PollEditClicked event navigates`() = runTest { + val navigator = FakeMessagesNavigator() + val presenter = createTimelinePresenter( + messagesNavigator = navigator, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitFirstItem().eventSink(TimelineEvents.PollEditClicked(AN_EVENT_ID)) + assertThat(navigator.onEditPollClickedCount).isEqualTo(1) } } @@ -326,47 +353,43 @@ class TimelinePresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) // skip initial state assertThat(redactedVoiceMessageManager.invocations.size).isEqualTo(0) + awaitFirstItem() awaitItem().let { assertThat(it.timelineItems).isNotEmpty() - assertThat(redactedVoiceMessageManager.invocations.size).isEqualTo(1) } + assertThat(redactedVoiceMessageManager.invocations.size).isEqualTo(1) } } + private suspend fun ReceiveTurbine.awaitFirstItem(): T { + // Skip 1 item if Mentions feature is enabled + if (FeatureFlags.Mentions.defaultValue) { + skipItems(1) + } + return awaitItem() + } + private fun TestScope.createTimelinePresenter( timeline: MatrixTimeline = FakeMatrixTimeline(), timelineItemsFactory: TimelineItemsFactory = aTimelineItemsFactory(), redactedVoiceMessageManager: RedactedVoiceMessageManager = FakeRedactedVoiceMessageManager(), + messagesNavigator: FakeMessagesNavigator = FakeMessagesNavigator(), + endPollAction: EndPollAction = FakeEndPollAction(), + sendPollResponseAction: SendPollResponseAction = FakeSendPollResponseAction(), ): TimelinePresenter { return TimelinePresenter( timelineItemsFactory = timelineItemsFactory, room = FakeMatrixRoom(matrixTimeline = timeline), dispatchers = testCoroutineDispatchers(), appScope = this, - analyticsService = FakeAnalyticsService(), + navigator = messagesNavigator, encryptionService = FakeEncryptionService(), verificationService = FakeSessionVerificationService(), featureFlagService = FakeFeatureFlagService(), redactedVoiceMessageManager = redactedVoiceMessageManager, - ) - } - - private fun TestScope.createTimelinePresenter( - room: MatrixRoom, - analyticsService: FakeAnalyticsService = FakeAnalyticsService(), - ): TimelinePresenter { - return TimelinePresenter( - timelineItemsFactory = aTimelineItemsFactory(), - room = room, - dispatchers = testCoroutineDispatchers(), - appScope = this, - analyticsService = analyticsService, - encryptionService = FakeEncryptionService(), - verificationService = FakeSessionVerificationService(), - featureFlagService = FakeFeatureFlagService(), - redactedVoiceMessageManager = FakeRedactedVoiceMessageManager(), + endPollAction = endPollAction, + sendPollResponseAction = sendPollResponseAction, ) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenterTests.kt similarity index 89% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenterTests.kt index 40912c0cc0..a6ef642672 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenterTests.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.timeline.components.customreaction +package io.element.android.features.messages.impl.timeline.components.customreaction import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow @@ -22,9 +22,6 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.impl.timeline.aTimelineItemEvent import io.element.android.features.messages.impl.timeline.aTimelineItemReactions -import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvents -import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionPresenter -import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.tests.testutils.WarmUpRule import kotlinx.coroutines.test.runTest diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/FakeEmojibaseProvider.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/FakeEmojibaseProvider.kt similarity index 81% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/FakeEmojibaseProvider.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/FakeEmojibaseProvider.kt index 7bf993e2a4..96513c8ab7 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/FakeEmojibaseProvider.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/FakeEmojibaseProvider.kt @@ -14,10 +14,9 @@ * limitations under the License. */ -package io.element.android.features.messages.timeline.components.customreaction +package io.element.android.features.messages.impl.timeline.components.customreaction import io.element.android.emojibasebindings.EmojibaseStore -import io.element.android.features.messages.impl.timeline.components.customreaction.EmojibaseProvider class FakeEmojibaseProvider: EmojibaseProvider { override val emojibaseStore: EmojibaseStore diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt similarity index 87% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt index 7b18d890df..d7d51c5eb4 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt @@ -14,14 +14,12 @@ * limitations under the License. */ -package io.element.android.features.messages.timeline.components.reactionsummary +package io.element.android.features.messages.impl.timeline.components.reactionsummary import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents -import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryPresenter import io.element.android.features.messages.impl.timeline.model.anAggregatedReaction import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.test.AN_AVATAR_URL @@ -31,6 +29,7 @@ import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.aRoomMember import io.element.android.tests.testutils.WarmUpRule +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -44,7 +43,7 @@ class ReactionSummaryPresenterTests { private val roomMember = aRoomMember(userId = A_USER_ID, avatarUrl = AN_AVATAR_URL, displayName = A_USER_NAME) private val summaryEvent = ReactionSummaryEvents.ShowReactionSummary(AN_EVENT_ID, listOf(aggregatedReaction), aggregatedReaction.key) private val room = FakeMatrixRoom().apply { - givenRoomMembersState(MatrixRoomMembersState.Ready(listOf(roomMember))) + givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember))) } private val presenter = ReactionSummaryPresenter(room) @@ -54,7 +53,7 @@ class ReactionSummaryPresenterTests { presenter.present() }.test { val initialState = awaitItem() - assertThat(initialState.target).isEqualTo(null) + assertThat(initialState.target).isNull() initialState.eventSink(summaryEvent) assertThat(awaitItem().target).isNotNull() @@ -70,7 +69,7 @@ class ReactionSummaryPresenterTests { presenter.present() }.test { val initialState = awaitItem() - assertThat(initialState.target).isEqualTo(null) + assertThat(initialState.target).isNull() initialState.eventSink(summaryEvent) val reactions = awaitItem().target?.reactions diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetPresenterTests.kt similarity index 87% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetPresenterTests.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetPresenterTests.kt index 248fcad399..ef98e3b3d9 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetPresenterTests.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetPresenterTests.kt @@ -14,15 +14,13 @@ * limitations under the License. */ -package io.element.android.features.messages.timeline.components.receipt.bottomsheet +package io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.impl.timeline.aTimelineItemEvent -import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents -import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetPresenter import io.element.android.tests.testutils.WarmUpRule import kotlinx.coroutines.test.runTest import org.junit.Rule diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt similarity index 95% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt index 011db1f038..4278679297 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt @@ -14,15 +14,13 @@ * limitations under the License. */ -package io.element.android.features.messages.timeline.components.retrysendmenu +package io.element.android.features.messages.impl.timeline.components.retrysendmenu import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.impl.timeline.aTimelineItemEvent -import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuEvents -import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuPresenter import io.element.android.libraries.matrix.test.A_TRANSACTION_ID import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.tests.testutils.WarmUpRule diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt new file mode 100644 index 0000000000..0fe79b8963 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt @@ -0,0 +1,630 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.factories.event + +import android.text.SpannableString +import android.text.Spanned +import android.text.style.URLSpan +import androidx.core.text.buildSpannedString +import androidx.core.text.inSpans +import com.google.common.truth.Truth.assertThat +import io.element.android.features.location.api.Location +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEmoteContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemNoticeContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent +import io.element.android.features.messages.test.timeline.FakeHtmlConverterProvider +import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter +import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags +import io.element.android.libraries.featureflag.test.FakeFeatureFlagService +import io.element.android.libraries.matrix.api.media.AudioDetails +import io.element.android.libraries.matrix.api.media.AudioInfo +import io.element.android.libraries.matrix.api.media.FileInfo +import io.element.android.libraries.matrix.api.media.ImageInfo +import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrix.api.media.ThumbnailInfo +import io.element.android.libraries.matrix.api.media.VideoInfo +import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody +import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo +import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat +import io.element.android.libraries.matrix.api.timeline.item.event.MessageType +import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.OtherMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.ui.components.A_BLUR_HASH +import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorWithoutValidation +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import kotlin.time.Duration +import kotlin.time.Duration.Companion.minutes + +@RunWith(RobolectricTestRunner::class) +class TimelineItemContentMessageFactoryTest { + + @Test + fun `test create OtherMessageType`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent(type = OtherMessageType(msgType = "a_type", body = "body")), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + val expected = TimelineItemTextContent( + body = "body", + htmlDocument = null, + plainText = "body", + isEdited = false, + formattedBody = null, + ) + assertThat(result).isEqualTo(expected) + } + + @Test + fun `test create LocationMessageType not null`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent(type = LocationMessageType("body", "geo:1,2", "description")), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + val expected = TimelineItemLocationContent( + body = "body", + location = Location(lat = 1.0, lon = 2.0, accuracy = 0.0F), + description = "description", + ) + assertThat(result).isEqualTo(expected) + } + + @Test + fun `test create LocationMessageType null`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent(type = LocationMessageType("body", "", null)), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + val expected = TimelineItemTextContent( + body = "body", + htmlDocument = null, + plainText = "body", + isEdited = false, + formattedBody = null, + ) + assertThat(result).isEqualTo(expected) + } + + @Test + fun `test create TextMessageType`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent(type = TextMessageType("body", null)), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + val expected = TimelineItemTextContent( + body = "body", + htmlDocument = null, + plainText = "body", + isEdited = false, + formattedBody = null, + ) + assertThat(result).isEqualTo(expected) + } + + @Test + fun `test create TextMessageType with simple link`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent(type = TextMessageType("https://www.example.org", null)), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) as TimelineItemTextContent + val expected = TimelineItemTextContent( + body = "https://www.example.org", + htmlDocument = null, + plainText = "https://www.example.org", + isEdited = false, + formattedBody = buildSpannedString { + inSpans(URLSpan("https://www.example.org")) { + append("https://www.example.org") + } + } + ) + assertThat(result.body).isEqualTo(expected.body) + assertThat(result.htmlDocument).isEqualTo(expected.htmlDocument) + assertThat(result.plainText).isEqualTo(expected.plainText) + assertThat(result.isEdited).isEqualTo(expected.isEdited) + assertThat(result.formattedBody).isInstanceOf(Spanned::class.java) + val spanned = result.formattedBody as Spanned + assertThat(spanned.toString()).isEqualTo("https://www.example.org") + val urlSpans = spanned.getSpans(0, spanned.length, URLSpan::class.java) + assertThat(urlSpans).hasLength(1) + assertThat(urlSpans[0].url).isEqualTo("https://www.example.org") + } + + @Test + fun `test create TextMessageType with HTML formatted body`() = runTest { + val expected = buildSpannedString { + append("link to ") + inSpans(URLSpan("https://matrix.org")) { + append("https://matrix.org") + } + append(" ") + inSpans(URLSpan("https://matrix.org")) { + append("and manually added link") + } + } + val sut = createTimelineItemContentMessageFactory( + htmlConverterTransform = { expected } + ) + val result = sut.create( + content = createMessageContent( + type = TextMessageType( + body = "body", + formatted = FormattedBody(MessageFormat.HTML, expected.toString()) + ) + ), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + assertThat((result as TimelineItemTextContent).formattedBody).isEqualTo(expected) + } + + @Test + fun `test create TextMessageType with unknown formatted body does nothing`() = runTest { + val sut = createTimelineItemContentMessageFactory( + htmlConverterTransform = { it } + ) + val result = sut.create( + content = createMessageContent( + type = TextMessageType( + body = "body", + formatted = FormattedBody(MessageFormat.UNKNOWN, "formatted") + ) + ), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + assertThat((result as TimelineItemTextContent).formattedBody).isNull() + } + + @Test + fun `test create VideoMessageType`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent(type = VideoMessageType("body", MediaSource("url"), null)), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + val expected = TimelineItemVideoContent( + body = "body", + duration = Duration.ZERO, + videoSource = MediaSource(url = "url", json = null), + thumbnailSource = null, + aspectRatio = null, + blurHash = null, + height = null, + width = null, + mimeType = MimeTypes.OctetStream, + formattedFileSize = "0 Bytes", + fileExtension = "", + ) + assertThat(result).isEqualTo(expected) + } + + @Test + fun `test create VideoMessageType with info`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent( + type = VideoMessageType( + body = "body.mp4", + source = MediaSource("url"), + info = VideoInfo( + duration = 1.minutes, + height = 100, + width = 300, + mimetype = MimeTypes.Mp4, + size = 555, + thumbnailInfo = ThumbnailInfo( + height = 10L, + width = 5L, + mimetype = MimeTypes.Jpeg, + size = 111L, + ), + thumbnailSource = MediaSource("url_thumbnail"), + blurhash = A_BLUR_HASH, + ), + ) + ), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + val expected = TimelineItemVideoContent( + body = "body.mp4", + duration = 1.minutes, + videoSource = MediaSource(url = "url", json = null), + thumbnailSource = MediaSource("url_thumbnail"), + aspectRatio = 3f, + blurHash = A_BLUR_HASH, + height = 100, + width = 300, + mimeType = MimeTypes.Mp4, + formattedFileSize = "555 Bytes", + fileExtension = "mp4", + ) + assertThat(result).isEqualTo(expected) + } + + @Test + fun `test create AudioMessageType`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent(type = AudioMessageType("body", MediaSource("url"), null)), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + val expected = TimelineItemAudioContent( + body = "body", + duration = Duration.ZERO, + mediaSource = MediaSource(url = "url", json = null), + mimeType = MimeTypes.OctetStream, + formattedFileSize = "0 Bytes", + fileExtension = "", + ) + assertThat(result).isEqualTo(expected) + } + + @Test + fun `test create AudioMessageType with info`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent( + type = AudioMessageType( + body = "body.mp3", + source = MediaSource("url"), + info = AudioInfo( + duration = 1.minutes, + size = 123L, + mimetype = MimeTypes.Mp3, + ) + ) + ), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + val expected = TimelineItemAudioContent( + body = "body.mp3", + duration = 1.minutes, + mediaSource = MediaSource(url = "url", json = null), + mimeType = MimeTypes.Mp3, + formattedFileSize = "123 Bytes", + fileExtension = "mp3", + ) + assertThat(result).isEqualTo(expected) + } + + @Test + fun `test create VoiceMessageType`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent(type = VoiceMessageType("body", MediaSource("url"), null, null)), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + val expected = TimelineItemVoiceContent( + eventId = AN_EVENT_ID, + body = "body", + duration = Duration.ZERO, + mediaSource = MediaSource(url = "url", json = null), + mimeType = MimeTypes.OctetStream, + waveform = emptyList().toImmutableList() + ) + assertThat(result).isEqualTo(expected) + } + + @Test + fun `test create VoiceMessageType with info`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent( + type = VoiceMessageType( + body = "body.ogg", + source = MediaSource("url"), + info = AudioInfo( + duration = 1.minutes, + size = 123L, + mimetype = MimeTypes.Ogg, + ), + details = AudioDetails( + duration = 1.minutes, + waveform = persistentListOf(1f, 2f), + ), + ) + ), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + val expected = TimelineItemVoiceContent( + eventId = AN_EVENT_ID, + body = "body.ogg", + duration = 1.minutes, + mediaSource = MediaSource(url = "url", json = null), + mimeType = MimeTypes.Ogg, + waveform = persistentListOf(1f, 2f) + ) + assertThat(result).isEqualTo(expected) + } + + @Test + fun `test create VoiceMessageType feature disabled`() = runTest { + val sut = createTimelineItemContentMessageFactory( + featureFlagService = FakeFeatureFlagService( + initialState = mapOf( + FeatureFlags.VoiceMessages.key to false, + ) + ) + ) + val result = sut.create( + content = createMessageContent(type = VoiceMessageType("body", MediaSource("url"), null, null)), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + val expected = TimelineItemAudioContent( + body = "body", + duration = Duration.ZERO, + mediaSource = MediaSource(url = "url", json = null), + mimeType = MimeTypes.OctetStream, + formattedFileSize = "0 Bytes", + fileExtension = "" + ) + assertThat(result).isEqualTo(expected) + } + + @Test + fun `test create ImageMessageType`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent(type = ImageMessageType("body", MediaSource("url"), null)), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + val expected = TimelineItemImageContent( + body = "body", + mediaSource = MediaSource(url = "url", json = null), + thumbnailSource = null, + formattedFileSize = "0 Bytes", + fileExtension = "", + mimeType = MimeTypes.OctetStream, + blurhash = null, + width = null, + height = null, + aspectRatio = null + ) + assertThat(result).isEqualTo(expected) + } + + @Test + fun `test create ImageMessageType with info`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent( + type = ImageMessageType( + body = "body.jpg", + source = MediaSource("url"), + info = ImageInfo( + height = 10L, + width = 5L, + mimetype = MimeTypes.Jpeg, + size = 888L, + thumbnailInfo = ThumbnailInfo( + height = 10L, + width = 5L, + mimetype = MimeTypes.Jpeg, + size = 111L, + ), + thumbnailSource = MediaSource("url_thumbnail"), + blurhash = A_BLUR_HASH, + ) + ) + ), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + val expected = TimelineItemImageContent( + body = "body.jpg", + mediaSource = MediaSource(url = "url", json = null), + thumbnailSource = MediaSource("url_thumbnail"), + formattedFileSize = "888 Bytes", + fileExtension = "jpg", + mimeType = MimeTypes.Jpeg, + blurhash = A_BLUR_HASH, + width = 5, + height = 10, + aspectRatio = 0.5f, + ) + assertThat(result).isEqualTo(expected) + } + + @Test + fun `test create FileMessageType`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent(type = FileMessageType("body", MediaSource("url"), null)), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + val expected = TimelineItemFileContent( + body = "body", + fileSource = MediaSource(url = "url", json = null), + thumbnailSource = null, + formattedFileSize = "0 Bytes", + fileExtension = "", + mimeType = MimeTypes.OctetStream + ) + assertThat(result).isEqualTo(expected) + } + + @Test + fun `test create FileMessageType with info`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent( + type = FileMessageType( + body = "body.pdf", + source = MediaSource("url"), + info = FileInfo( + mimetype = MimeTypes.Pdf, + size = 123L, + thumbnailInfo = ThumbnailInfo( + height = 10L, + width = 5L, + mimetype = MimeTypes.Jpeg, + size = 111L, + ), + thumbnailSource = MediaSource("url_thumbnail"), + ) + ) + ), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + val expected = TimelineItemFileContent( + body = "body.pdf", + fileSource = MediaSource(url = "url", json = null), + thumbnailSource = MediaSource("url_thumbnail"), + formattedFileSize = "123 Bytes", + fileExtension = "pdf", + mimeType = MimeTypes.Pdf + ) + assertThat(result).isEqualTo(expected) + } + + @Test + fun `test create NoticeMessageType`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent(type = NoticeMessageType("body", null)), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + val expected = TimelineItemNoticeContent( + body = "body", + htmlDocument = null, + plainText = "body", + formattedBody = null, + isEdited = false, + ) + assertThat(result).isEqualTo(expected) + } + + @Test + fun `test create NoticeMessageType with HTML formatted body`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent( + type = NoticeMessageType( + body = "body", + formatted = FormattedBody(MessageFormat.HTML, "formatted") + ) + ), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + assertThat((result as TimelineItemNoticeContent).formattedBody).isEqualTo("formatted") + } + + @Test + fun `test create EmoteMessageType`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent(type = EmoteMessageType("body", null)), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + val expected = TimelineItemEmoteContent( + body = "* Bob body", + htmlDocument = null, + plainText = "* Bob body", + formattedBody = null, + isEdited = false, + ) + assertThat(result).isEqualTo(expected) + } + + @Test + fun `test create EmoteMessageType with HTML formatted body`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent( + type = EmoteMessageType( + body = "body", + formatted = FormattedBody(MessageFormat.HTML, "formatted") + ) + ), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) + assertThat((result as TimelineItemEmoteContent).formattedBody).isEqualTo(SpannableString("* Bob formatted")) + } + + private fun createMessageContent( + body: String = "Body", + inReplyTo: InReplyTo? = null, + isEdited: Boolean = false, + isThreaded: Boolean = false, + type: MessageType, + ): MessageContent { + return MessageContent( + body = body, + inReplyTo = inReplyTo, + isEdited = isEdited, + isThreaded = isThreaded, + type = type, + ) + } + + private fun createTimelineItemContentMessageFactory( + featureFlagService: FeatureFlagService = FakeFeatureFlagService(), + htmlConverterTransform: (String) -> CharSequence = { it }, + ) = TimelineItemContentMessageFactory( + fileSizeFormatter = FakeFileSizeFormatter(), + fileExtensionExtractor = FileExtensionExtractorWithoutValidation(), + featureFlagService = featureFlagService, + htmlConverterProvider = FakeHtmlConverterProvider(htmlConverterTransform), + ) +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt similarity index 87% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt index e8a8eb30cf..42d4b3388a 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt @@ -14,13 +14,12 @@ * limitations under the License. */ -package io.element.android.features.messages.timeline.groups +package io.element.android.features.messages.impl.timeline.groups import com.google.common.truth.Truth.assertThat -import io.element.android.features.messages.fixtures.aMessageEvent +import io.element.android.features.messages.impl.fixtures.aMessageEvent +import io.element.android.features.messages.impl.timeline.aTimelineItemDebugInfo import io.element.android.features.messages.impl.timeline.aTimelineItemReactions -import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper -import io.element.android.features.messages.impl.timeline.groups.computeGroupIdWith import io.element.android.features.messages.impl.timeline.model.ReadReceiptData import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts @@ -30,7 +29,6 @@ import io.element.android.libraries.designsystem.components.avatar.anAvatarData import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_USER_ID -import io.element.android.libraries.matrix.test.room.aTimelineItemDebugInfo import kotlinx.collections.immutable.toImmutableList import org.junit.Test @@ -46,6 +44,7 @@ class TimelineItemGrouperTest { reactionsState = aTimelineItemReactions(count = 0), readReceiptState = TimelineItemReadReceipts(emptyList().toImmutableList()), localSendState = LocalEventSendState.Sent(AN_EVENT_ID), + isEditable = false, inReplyTo = null, isThreaded = false, debugInfo = aTimelineItemDebugInfo(), @@ -87,11 +86,12 @@ class TimelineItemGrouperTest { assertThat(result).isEqualTo( listOf( TimelineItem.GroupedEvents( - computeGroupIdWith(aGroupableItem), + id = computeGroupIdWith(aGroupableItem), events = listOf( aGroupableItem.copy("0"), aGroupableItem.copy(id = "1"), - ).toImmutableList() + ).toImmutableList(), + aggregatedReadReceipts = emptyList().toImmutableList(), ), ) ) @@ -133,20 +133,22 @@ class TimelineItemGrouperTest { assertThat(result).isEqualTo( listOf( TimelineItem.GroupedEvents( - computeGroupIdWith(aGroupableItem), + id = computeGroupIdWith(aGroupableItem), events = listOf( aGroupableItem, aGroupableItem, - ).toImmutableList() + ).toImmutableList(), + aggregatedReadReceipts = emptyList().toImmutableList(), ), aNonGroupableItem, TimelineItem.GroupedEvents( - computeGroupIdWith(aGroupableItem), + id = computeGroupIdWith(aGroupableItem), events = listOf( aGroupableItem, aGroupableItem, aGroupableItem, - ).toImmutableList() + ).toImmutableList(), + aggregatedReadReceipts = emptyList().toImmutableList(), ) ) ) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/model/AggregatedReactionTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionTest.kt similarity index 89% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/model/AggregatedReactionTest.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionTest.kt index ce107f76aa..e804d2b82a 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/model/AggregatedReactionTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionTest.kt @@ -14,9 +14,8 @@ * limitations under the License. */ -package io.element.android.features.messages.timeline.model +package io.element.android.features.messages.impl.timeline.model -import io.element.android.features.messages.impl.timeline.model.anAggregatedReaction import org.junit.Assert.assertEquals import org.junit.Test diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToDetailTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToDetailTest.kt new file mode 100644 index 0000000000..33a88cb741 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToDetailTest.kt @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.model + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody +import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo +import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange +import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat +import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent +import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_USER_ID +import org.junit.Test + +class InReplyToDetailTest { + + @Test + fun `map - with a not ready InReplyTo does not work`() { + assertThat(InReplyTo.Pending.map()).isNull() + assertThat(InReplyTo.NotLoaded(AN_EVENT_ID).map()).isNull() + assertThat(InReplyTo.Error.map()).isNull() + } + + @Test + fun `map - with something other than a MessageContent has no textContent`() { + val inReplyTo = InReplyTo.Ready( + eventId = AN_EVENT_ID, + senderId = A_USER_ID, + senderDisplayName = "senderDisplayName", + senderAvatarUrl = "senderAvatarUrl", + content = RoomMembershipContent( + userId = A_USER_ID, + change = MembershipChange.INVITED, + ) + ) + val inReplyToDetails = inReplyTo.map() + assertThat(inReplyToDetails).isNotNull() + assertThat(inReplyToDetails?.textContent).isNull() + } + + @Test + fun `map - with a message content tries to use the formatted text if exists for its textContent`() { + val inReplyTo = InReplyTo.Ready( + eventId = AN_EVENT_ID, + senderId = A_USER_ID, + senderDisplayName = "senderDisplayName", + senderAvatarUrl = "senderAvatarUrl", + content = MessageContent( + body = "**Hello!**", + inReplyTo = null, + isEdited = false, + isThreaded = false, + type = TextMessageType( + body = "**Hello!**", + formatted = FormattedBody( + format = MessageFormat.HTML, + body = "

Hello!

" + ) + ) + ) + ) + assertThat(inReplyTo.map()?.textContent).isEqualTo("Hello!") + } + + @Test + fun `map - with a message content and no formatted body uses body as fallback for textContent`() { + val inReplyTo = InReplyTo.Ready( + eventId = AN_EVENT_ID, + senderId = A_USER_ID, + senderDisplayName = "senderDisplayName", + senderAvatarUrl = "senderAvatarUrl", + content = MessageContent( + body = "**Hello!**", + inReplyTo = null, + isEdited = false, + isThreaded = false, + type = TextMessageType( + body = "**Hello!**", + formatted = null, + ) + ) + ) + assertThat(inReplyTo.map()?.textContent).isEqualTo("**Hello!**") + } +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToMetadataKtTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToMetadataKtTest.kt new file mode 100644 index 0000000000..cb79f5cd08 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToMetadataKtTest.kt @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.model + +import android.content.res.Configuration +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalContext +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import app.cash.molecule.RecompositionMode +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.media.AudioInfo +import io.element.android.libraries.matrix.api.media.FileInfo +import io.element.android.libraries.matrix.api.media.VideoInfo +import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.EventContent +import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent +import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.media.aMediaSource +import io.element.android.libraries.matrix.test.timeline.aMessageContent +import io.element.android.libraries.matrix.test.timeline.aPollContent +import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo +import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class InReplyToMetadataKtTest { + @Test + fun `any message content`() = runTest { + moleculeFlow(RecompositionMode.Immediate) { + anInReplyToDetails(eventContent = aMessageContent()).metadata() + }.test { + awaitItem().let { + assertThat(it).isEqualTo(InReplyToMetadata.Text("textContent")) + } + } + } + + @Test + fun `an image message content`() = runTest { + moleculeFlow(RecompositionMode.Immediate) { + anInReplyToDetails( + eventContent = aMessageContent( + messageType = ImageMessageType( + body = "body", + source = aMediaSource(), + info = null, + ) + ) + ).metadata() + }.test { + awaitItem().let { + assertThat(it).isEqualTo( + InReplyToMetadata.Thumbnail( + attachmentThumbnailInfo = AttachmentThumbnailInfo( + thumbnailSource = aMediaSource(), + textContent = "body", + type = AttachmentThumbnailType.Image, + blurHash = null, + ) + ) + ) + } + } + } + + @Test + fun `a video message content`() = runTest { + moleculeFlow(RecompositionMode.Immediate) { + anInReplyToDetails( + eventContent = aMessageContent( + messageType = VideoMessageType( + body = "body", + source = aMediaSource(), + info = VideoInfo( + duration = null, + height = null, + width = null, + mimetype = null, + size = null, + thumbnailInfo = null, + thumbnailSource = aMediaSource(), + blurhash = null + ), + ) + ) + ).metadata() + }.test { + awaitItem().let { + assertThat(it).isEqualTo( + InReplyToMetadata.Thumbnail( + attachmentThumbnailInfo = AttachmentThumbnailInfo( + thumbnailSource = aMediaSource(), + textContent = "body", + type = AttachmentThumbnailType.Video, + blurHash = null, + ) + ) + ) + } + } + } + + @Test + fun `a file message content`() = runTest { + moleculeFlow(RecompositionMode.Immediate) { + anInReplyToDetails( + eventContent = aMessageContent( + messageType = FileMessageType( + body = "body", + source = aMediaSource(), + info = FileInfo( + mimetype = null, + size = null, + thumbnailInfo = null, + thumbnailSource = aMediaSource(), + ), + ) + ) + ).metadata() + }.test { + awaitItem().let { + assertThat(it).isEqualTo( + InReplyToMetadata.Thumbnail( + attachmentThumbnailInfo = AttachmentThumbnailInfo( + thumbnailSource = aMediaSource(), + textContent = "body", + type = AttachmentThumbnailType.File, + blurHash = null, + ) + ) + ) + } + } + } + + @Test + fun `a audio message content`() = runTest { + moleculeFlow(RecompositionMode.Immediate) { + anInReplyToDetails( + eventContent = aMessageContent( + messageType = AudioMessageType( + body = "body", + source = aMediaSource(), + info = AudioInfo( + duration = null, + size = null, + mimetype = null + ), + ) + ) + ).metadata() + }.test { + awaitItem().let { + assertThat(it).isEqualTo( + InReplyToMetadata.Thumbnail( + attachmentThumbnailInfo = AttachmentThumbnailInfo( + textContent = "body", + type = AttachmentThumbnailType.Audio, + blurHash = null, + ) + ) + ) + } + } + } + + @Test + fun `a location message content`() = runTest { + moleculeFlow(RecompositionMode.Immediate) { + testEnv { + anInReplyToDetails( + eventContent = aMessageContent( + messageType = LocationMessageType( + body = "body", + geoUri = "geo:3.0,4.0;u=5.0", + description = null, + ) + ) + ).metadata() + } + }.test { + awaitItem().let { + assertThat(it).isEqualTo( + InReplyToMetadata.Thumbnail( + attachmentThumbnailInfo = AttachmentThumbnailInfo( + thumbnailSource = null, + textContent = "Shared location", + type = AttachmentThumbnailType.Location, + blurHash = null, + ) + ) + ) + } + } + } + + @Test + fun `a voice message content`() = runTest { + moleculeFlow(RecompositionMode.Immediate) { + testEnv { + anInReplyToDetails( + eventContent = aMessageContent( + messageType = VoiceMessageType( + body = "body", + source = aMediaSource(), + info = null, + details = null, + ) + ) + ).metadata() + } + }.test { + awaitItem().let { + assertThat(it).isEqualTo( + InReplyToMetadata.Thumbnail( + attachmentThumbnailInfo = AttachmentThumbnailInfo( + thumbnailSource = null, + textContent = "Voice message", + type = AttachmentThumbnailType.Voice, + blurHash = null, + ) + ) + ) + } + } + } + + @Test + fun `a poll content`() = runTest { + moleculeFlow(RecompositionMode.Immediate) { + anInReplyToDetails( + eventContent = aPollContent() + ).metadata() + }.test { + awaitItem().let { + assertThat(it).isEqualTo( + InReplyToMetadata.Thumbnail( + attachmentThumbnailInfo = AttachmentThumbnailInfo( + thumbnailSource = null, + textContent = "Do you like polls?", + type = AttachmentThumbnailType.Poll, + blurHash = null, + ) + ) + ) + } + } + } + + @Test + fun `any other content`() = runTest { + moleculeFlow(RecompositionMode.Immediate) { + anInReplyToDetails( + eventContent = RedactedContent + ).metadata() + }.test { + awaitItem().let { + assertThat(it).isNull() + } + } + } +} + +fun anInReplyToDetails( + eventId: EventId = AN_EVENT_ID, + senderId: UserId = A_USER_ID, + senderDisplayName: String? = "senderDisplayName", + senderAvatarUrl: String? = "senderAvatarUrl", + eventContent: EventContent? = aMessageContent(), + textContent: String? = "textContent", +) = InReplyToDetails( + eventId = eventId, + senderId = senderId, + senderDisplayName = senderDisplayName, + senderAvatarUrl = senderAvatarUrl, + eventContent = eventContent, + textContent = textContent, +) + +@Composable +private fun testEnv(content: @Composable () -> Any?): Any? { + var result: Any? = null + CompositionLocalProvider( + LocalConfiguration provides Configuration(), + LocalContext provides ApplicationProvider.getApplicationContext(), + ) { + content().apply { + result = this + } + } + return result +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenterTest.kt similarity index 98% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenterTest.kt index 4ec1432377..4a659f4ce3 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenterTest.kt @@ -16,7 +16,7 @@ @file:OptIn(ExperimentalCoroutinesApi::class) -package io.element.android.features.messages.voicemessages.composer +package io.element.android.features.messages.impl.voicemessages.composer import android.Manifest import androidx.lifecycle.Lifecycle @@ -27,10 +27,6 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.Composer import io.element.android.features.messages.impl.voicemessages.VoiceMessageException -import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerEvents -import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPlayer -import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPresenter -import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState import io.element.android.features.messages.test.FakeMessageComposerContext import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_MESSAGE @@ -44,8 +40,8 @@ import io.element.android.libraries.permissions.api.aPermissionsState import io.element.android.libraries.permissions.test.FakePermissionsPresenter import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory import io.element.android.libraries.textcomposer.model.MessageComposerMode -import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent import io.element.android.libraries.textcomposer.model.VoiceMessagePlayerEvent +import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent import io.element.android.libraries.textcomposer.model.VoiceMessageState import io.element.android.libraries.voicerecorder.test.FakeVoiceRecorder import io.element.android.services.analytics.test.FakeAnalyticsService diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt similarity index 81% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt index 152090b14c..0208d06e95 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt @@ -14,12 +14,13 @@ * limitations under the License. */ -package io.element.android.features.messages.voicemessages.timeline +package io.element.android.features.messages.impl.voicemessages.timeline -import com.google.common.truth.Truth -import io.element.android.features.messages.impl.voicemessages.timeline.DefaultVoiceMessageMediaRepo +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrix.api.mxc.MxcTools import io.element.android.libraries.matrix.test.media.FakeMediaLoader import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -43,10 +44,10 @@ class DefaultVoiceMessageMediaRepoTest { ) repo.getMediaFile().let { result -> - Truth.assertThat(result.isSuccess).isTrue() + assertThat(result.isSuccess).isTrue() result.getOrThrow().let { file -> - Truth.assertThat(file.path).isEqualTo(temporaryFolder.cachedFilePath) - Truth.assertThat(file.exists()).isTrue() + assertThat(file.path).isEqualTo(temporaryFolder.cachedFilePath) + assertThat(file.exists()).isTrue() } } } @@ -62,9 +63,9 @@ class DefaultVoiceMessageMediaRepoTest { ) repo.getMediaFile().let { result -> - Truth.assertThat(result.isFailure).isTrue() + assertThat(result.isFailure).isTrue() result.exceptionOrNull()!!.let { exception -> - Truth.assertThat(exception).isInstanceOf(RuntimeException::class.java) + assertThat(exception).isInstanceOf(RuntimeException::class.java) } } } @@ -87,9 +88,9 @@ class DefaultVoiceMessageMediaRepoTest { ) repo.getMediaFile().let { result -> - Truth.assertThat(result.isFailure).isTrue() + assertThat(result.isFailure).isTrue() result.exceptionOrNull()?.let { exception -> - Truth.assertThat(exception).apply { + assertThat(exception).apply { isInstanceOf(IllegalStateException::class.java) hasMessageThat().isEqualTo("Failed to move file to cache.") } @@ -109,10 +110,10 @@ class DefaultVoiceMessageMediaRepoTest { ) repo.getMediaFile().let { result -> - Truth.assertThat(result.isSuccess).isTrue() + assertThat(result.isSuccess).isTrue() result.getOrThrow().let { file -> - Truth.assertThat(file.path).isEqualTo(temporaryFolder.cachedFilePath) - Truth.assertThat(file.exists()).isTrue() + assertThat(file.path).isEqualTo(temporaryFolder.cachedFilePath) + assertThat(file.exists()).isTrue() } } } @@ -124,10 +125,10 @@ class DefaultVoiceMessageMediaRepoTest { mxcUri = INVALID_MXC_URI, ) repo.getMediaFile().let { result -> - Truth.assertThat(result.isFailure).isTrue() + assertThat(result.isFailure).isTrue() result.exceptionOrNull()!!.let { exception -> - Truth.assertThat(exception).isInstanceOf(RuntimeException::class.java) - Truth.assertThat(exception).hasMessageThat().isEqualTo("Invalid mxcUri.") + assertThat(exception).isInstanceOf(RuntimeException::class.java) + assertThat(exception).hasMessageThat().isEqualTo("Invalid mxcUri.") } } } @@ -139,12 +140,13 @@ private fun createDefaultVoiceMessageMediaRepo( mxcUri: String = MXC_URI, ) = DefaultVoiceMessageMediaRepo( cacheDir = temporaryFolder.root, + mxcTools = MxcTools(), matrixMediaLoader = matrixMediaLoader, mediaSource = MediaSource( url = mxcUri, json = null ), - mimeType = "audio/ogg", + mimeType = MimeTypes.Ogg, body = "someBody.ogg" ) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt similarity index 54% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt index e989a07b15..eb2c08d2be 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt @@ -14,14 +14,12 @@ * limitations under the License. */ -package io.element.android.features.messages.voicemessages.timeline +package io.element.android.features.messages.impl.voicemessages.timeline import app.cash.turbine.TurbineTestContext import app.cash.turbine.test -import com.google.common.truth.Truth -import io.element.android.features.messages.impl.voicemessages.timeline.DefaultVoiceMessagePlayer -import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessageMediaRepo -import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessagePlayer +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.test.AN_EVENT_ID @@ -44,7 +42,7 @@ class DefaultVoiceMessagePlayerTest { val player = createDefaultVoiceMessagePlayer() player.state.test { matchInitialState() - Truth.assertThat(player.prepare().isSuccess).isTrue() + assertThat(player.prepare().isSuccess).isTrue() matchReadyState() } } @@ -58,7 +56,7 @@ class DefaultVoiceMessagePlayerTest { ) player.state.test { matchInitialState() - Truth.assertThat(player.prepare().isFailure).isTrue() + assertThat(player.prepare().isFailure).isTrue() } } @@ -69,7 +67,7 @@ class DefaultVoiceMessagePlayerTest { ) player.state.test { matchInitialState() - Truth.assertThat(player.prepare().isFailure).isTrue() + assertThat(player.prepare().isFailure).isTrue() } } @@ -78,12 +76,12 @@ class DefaultVoiceMessagePlayerTest { val player = createDefaultVoiceMessagePlayer() player.state.test { matchInitialState() - Truth.assertThat(player.prepare().isSuccess).isTrue() + assertThat(player.prepare().isSuccess).isTrue() matchReadyState() player.play() awaitItem().let { - Truth.assertThat(it.isPlaying).isEqualTo(true) - Truth.assertThat(it.currentPosition).isEqualTo(1000) + assertThat(it.isPlaying).isTrue() + assertThat(it.currentPosition).isEqualTo(1000) } } } @@ -98,15 +96,15 @@ class DefaultVoiceMessagePlayerTest { ) player.state.test { matchInitialState() - Truth.assertThat(player.prepare().isSuccess).isTrue() + assertThat(player.prepare().isSuccess).isTrue() matchReadyState(fakeTotalDurationMs = 1000) player.play() awaitItem().let { - Truth.assertThat(it.isReady).isEqualTo(false) - Truth.assertThat(it.isPlaying).isEqualTo(false) - Truth.assertThat(it.isEnded).isEqualTo(true) - Truth.assertThat(it.currentPosition).isEqualTo(1000) - Truth.assertThat(it.duration).isEqualTo(1000) + assertThat(it.isReady).isFalse() + assertThat(it.isPlaying).isFalse() + assertThat(it.isEnded).isTrue() + assertThat(it.currentPosition).isEqualTo(1000) + assertThat(it.duration).isEqualTo(1000) } } } @@ -123,72 +121,72 @@ class DefaultVoiceMessagePlayerTest { // Play player1 until the end. player1.state.test { matchInitialState() - Truth.assertThat(player1.prepare().isSuccess).isTrue() + assertThat(player1.prepare().isSuccess).isTrue() matchReadyState(1_000L) player1.play() awaitItem().let { // it plays until the end. - Truth.assertThat(it.isReady).isEqualTo(false) - Truth.assertThat(it.isPlaying).isEqualTo(false) - Truth.assertThat(it.isEnded).isEqualTo(true) - Truth.assertThat(it.currentPosition).isEqualTo(1000) - Truth.assertThat(it.duration).isEqualTo(1000) + assertThat(it.isReady).isFalse() + assertThat(it.isPlaying).isFalse() + assertThat(it.isEnded).isTrue() + assertThat(it.currentPosition).isEqualTo(1000) + assertThat(it.duration).isEqualTo(1000) } } // Play player2 until the end. player2.state.test { matchInitialState() - Truth.assertThat(player2.prepare().isSuccess).isTrue() + assertThat(player2.prepare().isSuccess).isTrue() awaitItem().let { // Additional spurious state due to MediaPlayer owner change. - Truth.assertThat(it.isReady).isEqualTo(false) - Truth.assertThat(it.isPlaying).isEqualTo(false) - Truth.assertThat(it.isEnded).isEqualTo(true) - Truth.assertThat(it.currentPosition).isEqualTo(1000) - Truth.assertThat(it.duration).isEqualTo(1000) + assertThat(it.isReady).isFalse() + assertThat(it.isPlaying).isFalse() + assertThat(it.isEnded).isTrue() + assertThat(it.currentPosition).isEqualTo(1000) + assertThat(it.duration).isEqualTo(1000) } awaitItem().let {// Additional spurious state due to MediaPlayer owner change. - Truth.assertThat(it.isReady).isEqualTo(false) - Truth.assertThat(it.isPlaying).isEqualTo(false) - Truth.assertThat(it.isEnded).isEqualTo(false) - Truth.assertThat(it.currentPosition).isEqualTo(0) - Truth.assertThat(it.duration).isEqualTo(null) + assertThat(it.isReady).isFalse() + assertThat(it.isPlaying).isFalse() + assertThat(it.isEnded).isFalse() + assertThat(it.currentPosition).isEqualTo(0) + assertThat(it.duration).isNull() } matchReadyState(1_000L) player2.play() awaitItem().let { // it plays until the end. - Truth.assertThat(it.isReady).isEqualTo(false) - Truth.assertThat(it.isPlaying).isEqualTo(false) - Truth.assertThat(it.isEnded).isEqualTo(true) - Truth.assertThat(it.currentPosition).isEqualTo(1000) - Truth.assertThat(it.duration).isEqualTo(1000) + assertThat(it.isReady).isFalse() + assertThat(it.isPlaying).isFalse() + assertThat(it.isEnded).isTrue() + assertThat(it.currentPosition).isEqualTo(1000) + assertThat(it.duration).isEqualTo(1000) } } // Play player1 again. player1.state.test { awaitItem().let {// Last previous state/ - Truth.assertThat(it.isReady).isEqualTo(false) - Truth.assertThat(it.isPlaying).isEqualTo(false) - Truth.assertThat(it.isEnded).isEqualTo(true) - Truth.assertThat(it.currentPosition).isEqualTo(1000) - Truth.assertThat(it.duration).isEqualTo(1000) + assertThat(it.isReady).isFalse() + assertThat(it.isPlaying).isFalse() + assertThat(it.isEnded).isTrue() + assertThat(it.currentPosition).isEqualTo(1000) + assertThat(it.duration).isEqualTo(1000) } - Truth.assertThat(player1.prepare().isSuccess).isTrue() + assertThat(player1.prepare().isSuccess).isTrue() awaitItem().let {// Additional spurious state due to MediaPlayer owner change. - Truth.assertThat(it.isReady).isEqualTo(false) - Truth.assertThat(it.isPlaying).isEqualTo(false) - Truth.assertThat(it.isEnded).isEqualTo(false) - Truth.assertThat(it.currentPosition).isEqualTo(0) - Truth.assertThat(it.duration).isEqualTo(null) + assertThat(it.isReady).isFalse() + assertThat(it.isPlaying).isFalse() + assertThat(it.isEnded).isFalse() + assertThat(it.currentPosition).isEqualTo(0) + assertThat(it.duration).isNull() } matchReadyState(1_000L) player1.play() awaitItem().let { // it played again until the end. - Truth.assertThat(it.isReady).isEqualTo(false) - Truth.assertThat(it.isPlaying).isEqualTo(false) - Truth.assertThat(it.isEnded).isEqualTo(true) - Truth.assertThat(it.currentPosition).isEqualTo(1000) - Truth.assertThat(it.duration).isEqualTo(1000) + assertThat(it.isReady).isFalse() + assertThat(it.isPlaying).isFalse() + assertThat(it.isEnded).isTrue() + assertThat(it.currentPosition).isEqualTo(1000) + assertThat(it.duration).isEqualTo(1000) } } } @@ -198,14 +196,14 @@ class DefaultVoiceMessagePlayerTest { val player = createDefaultVoiceMessagePlayer() player.state.test { matchInitialState() - Truth.assertThat(player.prepare().isSuccess).isTrue() + assertThat(player.prepare().isSuccess).isTrue() matchReadyState() player.play() skipItems(1) // skip play state player.pause() awaitItem().let { - Truth.assertThat(it.isPlaying).isEqualTo(false) - Truth.assertThat(it.currentPosition).isEqualTo(1000) + assertThat(it.isPlaying).isFalse() + assertThat(it.currentPosition).isEqualTo(1000) } } } @@ -215,7 +213,7 @@ class DefaultVoiceMessagePlayerTest { val player = createDefaultVoiceMessagePlayer() player.state.test { matchInitialState() - Truth.assertThat(player.prepare().isSuccess).isTrue() + assertThat(player.prepare().isSuccess).isTrue() matchReadyState() player.play() skipItems(1) // skip play state @@ -223,8 +221,8 @@ class DefaultVoiceMessagePlayerTest { skipItems(1) // skip pause state player.play() awaitItem().let { - Truth.assertThat(it.isPlaying).isEqualTo(true) - Truth.assertThat(it.currentPosition).isEqualTo(2000) + assertThat(it.isPlaying).isTrue() + assertThat(it.currentPosition).isEqualTo(2000) } } } @@ -236,19 +234,19 @@ class DefaultVoiceMessagePlayerTest { matchInitialState() player.seekTo(2000) awaitItem().let { - Truth.assertThat(it.isReady).isEqualTo(false) - Truth.assertThat(it.isPlaying).isEqualTo(false) - Truth.assertThat(it.isEnded).isEqualTo(false) - Truth.assertThat(it.currentPosition).isEqualTo(2000) - Truth.assertThat(it.duration).isEqualTo(null) + assertThat(it.isReady).isFalse() + assertThat(it.isPlaying).isFalse() + assertThat(it.isEnded).isFalse() + assertThat(it.currentPosition).isEqualTo(2000) + assertThat(it.duration).isNull() } - Truth.assertThat(player.prepare().isSuccess).isTrue() + assertThat(player.prepare().isSuccess).isTrue() awaitItem().let { - Truth.assertThat(it.isReady).isEqualTo(true) - Truth.assertThat(it.isPlaying).isEqualTo(false) - Truth.assertThat(it.isEnded).isEqualTo(false) - Truth.assertThat(it.currentPosition).isEqualTo(2000) - Truth.assertThat(it.duration).isEqualTo(FAKE_TOTAL_DURATION_MS) + assertThat(it.isReady).isTrue() + assertThat(it.isPlaying).isFalse() + assertThat(it.isEnded).isFalse() + assertThat(it.currentPosition).isEqualTo(2000) + assertThat(it.duration).isEqualTo(FAKE_TOTAL_DURATION_MS) } } } @@ -258,15 +256,15 @@ class DefaultVoiceMessagePlayerTest { val player = createDefaultVoiceMessagePlayer() player.state.test { matchInitialState() - Truth.assertThat(player.prepare().isSuccess).isTrue() + assertThat(player.prepare().isSuccess).isTrue() matchReadyState() player.seekTo(2000) awaitItem().let { - Truth.assertThat(it.isReady).isEqualTo(true) - Truth.assertThat(it.isPlaying).isEqualTo(false) - Truth.assertThat(it.isEnded).isEqualTo(false) - Truth.assertThat(it.currentPosition).isEqualTo(2000) - Truth.assertThat(it.duration).isEqualTo(FAKE_TOTAL_DURATION_MS) + assertThat(it.isReady).isTrue() + assertThat(it.isPlaying).isFalse() + assertThat(it.isEnded).isFalse() + assertThat(it.currentPosition).isEqualTo(2000) + assertThat(it.duration).isEqualTo(FAKE_TOTAL_DURATION_MS) } } } @@ -290,7 +288,7 @@ private fun createDefaultVoiceMessagePlayer( url = MXC_URI, json = null ), - mimeType = "audio/ogg", + mimeType = MimeTypes.Ogg, body = "someBody.ogg" ) @@ -298,11 +296,11 @@ private const val MXC_URI = "mxc://matrix.org/1234567890abcdefg" private suspend fun TurbineTestContext.matchInitialState() { awaitItem().let { - Truth.assertThat(it.isReady).isEqualTo(false) - Truth.assertThat(it.isPlaying).isEqualTo(false) - Truth.assertThat(it.isEnded).isEqualTo(false) - Truth.assertThat(it.currentPosition).isEqualTo(0) - Truth.assertThat(it.duration).isEqualTo(null) + assertThat(it.isReady).isFalse() + assertThat(it.isPlaying).isFalse() + assertThat(it.isEnded).isFalse() + assertThat(it.currentPosition).isEqualTo(0) + assertThat(it.duration).isNull() } } @@ -310,10 +308,10 @@ private suspend fun TurbineTestContext.matchReadyState fakeTotalDurationMs: Long = FAKE_TOTAL_DURATION_MS, ) { awaitItem().let { - Truth.assertThat(it.isReady).isEqualTo(true) - Truth.assertThat(it.isPlaying).isEqualTo(false) - Truth.assertThat(it.isEnded).isEqualTo(false) - Truth.assertThat(it.currentPosition).isEqualTo(0) - Truth.assertThat(it.duration).isEqualTo(fakeTotalDurationMs) + assertThat(it.isReady).isTrue() + assertThat(it.isPlaying).isFalse() + assertThat(it.isEnded).isFalse() + assertThat(it.currentPosition).isEqualTo(0) + assertThat(it.duration).isEqualTo(fakeTotalDurationMs) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/FakeRedactedVoiceMessageManager.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/FakeRedactedVoiceMessageManager.kt similarity index 86% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/FakeRedactedVoiceMessageManager.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/FakeRedactedVoiceMessageManager.kt index 0ff58d7728..589847a3af 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/FakeRedactedVoiceMessageManager.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/FakeRedactedVoiceMessageManager.kt @@ -14,9 +14,8 @@ * limitations under the License. */ -package io.element.android.features.messages.voicemessages.timeline +package io.element.android.features.messages.impl.voicemessages.timeline -import io.element.android.features.messages.impl.voicemessages.timeline.RedactedVoiceMessageManager import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem class FakeRedactedVoiceMessageManager : RedactedVoiceMessageManager { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/FakeVoiceMessageMediaRepo.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/FakeVoiceMessageMediaRepo.kt similarity index 87% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/FakeVoiceMessageMediaRepo.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/FakeVoiceMessageMediaRepo.kt index 198b65e445..3fdef7eae8 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/FakeVoiceMessageMediaRepo.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/FakeVoiceMessageMediaRepo.kt @@ -14,9 +14,8 @@ * limitations under the License. */ -package io.element.android.features.messages.voicemessages.timeline +package io.element.android.features.messages.impl.voicemessages.timeline -import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessageMediaRepo import io.element.android.tests.testutils.simulateLongTask import java.io.File diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt similarity index 77% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt index ccf3d4aa92..4cabc6450a 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt @@ -14,10 +14,10 @@ * limitations under the License. */ -package io.element.android.features.messages.voicemessages.timeline +package io.element.android.features.messages.impl.voicemessages.timeline -import com.google.common.truth.Truth -import io.element.android.features.messages.impl.voicemessages.timeline.DefaultRedactedVoiceMessageManager +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo @@ -30,6 +30,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.mediaplayer.api.MediaPlayer import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Test @@ -38,35 +39,35 @@ class RedactedVoiceMessageManagerTest { @Test fun `redacted event - no playing related media`() = runTest { val mediaPlayer = FakeMediaPlayer().apply { - setMedia(uri = "someUri", mediaId = AN_EVENT_ID.value, mimeType = "audio/ogg") + setMedia(uri = "someUri", mediaId = AN_EVENT_ID.value, mimeType = MimeTypes.Ogg) play() } val manager = aDefaultRedactedVoiceMessageManager(mediaPlayer = mediaPlayer) - Truth.assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value) - Truth.assertThat(mediaPlayer.state.value.isPlaying).isTrue() + assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value) + assertThat(mediaPlayer.state.value.isPlaying).isTrue() manager.onEachMatrixTimelineItem(aRedactedMatrixTimeline(AN_EVENT_ID_2)) - Truth.assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value) - Truth.assertThat(mediaPlayer.state.value.isPlaying).isTrue() + assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value) + assertThat(mediaPlayer.state.value.isPlaying).isTrue() } @Test fun `redacted event - playing related media is paused`() = runTest { val mediaPlayer = FakeMediaPlayer().apply { - setMedia(uri = "someUri", mediaId = AN_EVENT_ID.value, mimeType = "audio/ogg") + setMedia(uri = "someUri", mediaId = AN_EVENT_ID.value, mimeType = MimeTypes.Ogg) play() } val manager = aDefaultRedactedVoiceMessageManager(mediaPlayer = mediaPlayer) - Truth.assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value) - Truth.assertThat(mediaPlayer.state.value.isPlaying).isTrue() + assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value) + assertThat(mediaPlayer.state.value.isPlaying).isTrue() manager.onEachMatrixTimelineItem(aRedactedMatrixTimeline(AN_EVENT_ID)) - Truth.assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value) - Truth.assertThat(mediaPlayer.state.value.isPlaying).isFalse() + assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value) + assertThat(mediaPlayer.state.value.isPlaying).isFalse() } } @@ -79,7 +80,7 @@ fun TestScope.aDefaultRedactedVoiceMessageManager( fun aRedactedMatrixTimeline(eventId: EventId) = listOf( MatrixTimelineItem.Event( - uniqueId = 0, + uniqueId = "0", event = EventTimelineItem( eventId = eventId, transactionId = null, @@ -88,8 +89,8 @@ fun aRedactedMatrixTimeline(eventId: EventId) = listOf( isOwn = false, isRemote = false, localSendState = null, - reactions = listOf(), - receipts = listOf(), + reactions = persistentListOf(), + receipts = persistentListOf(), sender = A_USER_ID, senderProfile = ProfileTimelineDetails.Unavailable, timestamp = 9442, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenterTest.kt similarity index 57% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt rename to features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenterTest.kt index 00c8cd1f70..00b0874696 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenterTest.kt @@ -14,26 +14,22 @@ * limitations under the License. */ -package io.element.android.features.messages.voicemessages.timeline +package io.element.android.features.messages.impl.voicemessages.timeline import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemVoiceContent import io.element.android.features.messages.impl.voicemessages.VoiceMessageException -import io.element.android.features.messages.impl.voicemessages.timeline.DefaultVoiceMessagePlayer -import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessageEvents -import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessageMediaRepo -import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessagePresenter -import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessageState import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.test.FakeAnalyticsService import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Test +import kotlin.time.Duration.Companion.milliseconds class VoiceMessagePresenterTest { @Test @@ -43,9 +39,9 @@ class VoiceMessagePresenterTest { presenter.present() }.test { awaitItem().let { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) - Truth.assertThat(it.progress).isEqualTo(0f) - Truth.assertThat(it.time).isEqualTo("1:01") + assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) + assertThat(it.progress).isEqualTo(0f) + assertThat(it.time).isEqualTo("1:01") } } } @@ -54,33 +50,33 @@ class VoiceMessagePresenterTest { fun `pressing play downloads and plays`() = runTest { val presenter = createVoiceMessagePresenter( mediaPlayer = FakeMediaPlayer(fakeTotalDurationMs = 2_000), - content = aTimelineItemVoiceContent(durationMs = 2_000), + content = aTimelineItemVoiceContent(duration = 2_000.milliseconds), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) - Truth.assertThat(it.progress).isEqualTo(0f) - Truth.assertThat(it.time).isEqualTo("0:02") + assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) + assertThat(it.progress).isEqualTo(0f) + assertThat(it.time).isEqualTo("0:02") } initialState.eventSink(VoiceMessageEvents.PlayPause) awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading) - Truth.assertThat(it.progress).isEqualTo(0f) - Truth.assertThat(it.time).isEqualTo("0:02") + assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading) + assertThat(it.progress).isEqualTo(0f) + assertThat(it.time).isEqualTo("0:02") } awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading) - Truth.assertThat(it.progress).isEqualTo(0f) - Truth.assertThat(it.time).isEqualTo("0:00") + assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading) + assertThat(it.progress).isEqualTo(0f) + assertThat(it.time).isEqualTo("0:00") } awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause) - Truth.assertThat(it.progress).isEqualTo(0.5f) - Truth.assertThat(it.time).isEqualTo("0:01") + assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause) + assertThat(it.progress).isEqualTo(0.5f) + assertThat(it.time).isEqualTo("0:01") } } } @@ -92,31 +88,31 @@ class VoiceMessagePresenterTest { mediaPlayer = FakeMediaPlayer(fakeTotalDurationMs = 2_000), voiceMessageMediaRepo = FakeVoiceMessageMediaRepo().apply { shouldFail = true }, analyticsService = analyticsService, - content = aTimelineItemVoiceContent(durationMs = 2_000), + content = aTimelineItemVoiceContent(duration = 2_000.milliseconds), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) - Truth.assertThat(it.progress).isEqualTo(0f) - Truth.assertThat(it.time).isEqualTo("0:02") + assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) + assertThat(it.progress).isEqualTo(0f) + assertThat(it.time).isEqualTo("0:02") } initialState.eventSink(VoiceMessageEvents.PlayPause) awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading) - Truth.assertThat(it.progress).isEqualTo(0f) - Truth.assertThat(it.time).isEqualTo("0:02") + assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading) + assertThat(it.progress).isEqualTo(0f) + assertThat(it.time).isEqualTo("0:02") } awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Retry) - Truth.assertThat(it.progress).isEqualTo(0f) - Truth.assertThat(it.time).isEqualTo("0:02") + assertThat(it.button).isEqualTo(VoiceMessageState.Button.Retry) + assertThat(it.progress).isEqualTo(0f) + assertThat(it.time).isEqualTo("0:02") } analyticsService.trackedErrors.first().also { - Truth.assertThat(it).apply { + assertThat(it).apply { isInstanceOf(VoiceMessageException.PlayMessageError::class.java) hasMessageThat().isEqualTo("Error while trying to play voice message") } @@ -128,31 +124,31 @@ class VoiceMessagePresenterTest { fun `pressing pause while playing pauses`() = runTest { val presenter = createVoiceMessagePresenter( mediaPlayer = FakeMediaPlayer(fakeTotalDurationMs = 2_000), - content = aTimelineItemVoiceContent(durationMs = 2_000), + content = aTimelineItemVoiceContent(duration = 2_000.milliseconds), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) - Truth.assertThat(it.progress).isEqualTo(0f) - Truth.assertThat(it.time).isEqualTo("0:02") + assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) + assertThat(it.progress).isEqualTo(0f) + assertThat(it.time).isEqualTo("0:02") } initialState.eventSink(VoiceMessageEvents.PlayPause) skipItems(2) // skip downloading states val playingState = awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause) - Truth.assertThat(it.progress).isEqualTo(0.5f) - Truth.assertThat(it.time).isEqualTo("0:01") + assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause) + assertThat(it.progress).isEqualTo(0.5f) + assertThat(it.time).isEqualTo("0:01") } playingState.eventSink(VoiceMessageEvents.PlayPause) awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) - Truth.assertThat(it.progress).isEqualTo(0.5f) - Truth.assertThat(it.time).isEqualTo("0:01") + assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) + assertThat(it.progress).isEqualTo(0.5f) + assertThat(it.time).isEqualTo("0:01") } } } @@ -166,9 +162,9 @@ class VoiceMessagePresenterTest { presenter.present() }.test { awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Disabled) - Truth.assertThat(it.progress).isEqualTo(0f) - Truth.assertThat(it.time).isEqualTo("1:01") + assertThat(it.button).isEqualTo(VoiceMessageState.Button.Disabled) + assertThat(it.progress).isEqualTo(0f) + assertThat(it.time).isEqualTo("1:01") } } } @@ -177,23 +173,23 @@ class VoiceMessagePresenterTest { fun `seeking before play`() = runTest { val presenter = createVoiceMessagePresenter( mediaPlayer = FakeMediaPlayer(fakeTotalDurationMs = 2_000), - content = aTimelineItemVoiceContent(durationMs = 10_000), + content = aTimelineItemVoiceContent(duration = 10_000.milliseconds), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) - Truth.assertThat(it.progress).isEqualTo(0f) - Truth.assertThat(it.time).isEqualTo("0:10") + assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) + assertThat(it.progress).isEqualTo(0f) + assertThat(it.time).isEqualTo("0:10") } initialState.eventSink(VoiceMessageEvents.Seek(0.5f)) awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) - Truth.assertThat(it.progress).isEqualTo(0.5f) - Truth.assertThat(it.time).isEqualTo("0:05") + assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) + assertThat(it.progress).isEqualTo(0.5f) + assertThat(it.time).isEqualTo("0:05") } } } @@ -201,15 +197,15 @@ class VoiceMessagePresenterTest { @Test fun `seeking after play`() = runTest { val presenter = createVoiceMessagePresenter( - content = aTimelineItemVoiceContent(durationMs = 10_000), + content = aTimelineItemVoiceContent(duration = 10_000.milliseconds), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) - Truth.assertThat(it.progress).isEqualTo(0f) - Truth.assertThat(it.time).isEqualTo("0:10") + assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) + assertThat(it.progress).isEqualTo(0f) + assertThat(it.time).isEqualTo("0:10") } initialState.eventSink(VoiceMessageEvents.PlayPause) @@ -217,17 +213,17 @@ class VoiceMessagePresenterTest { skipItems(2) // skip downloading states awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause) - Truth.assertThat(it.progress).isEqualTo(0.1f) - Truth.assertThat(it.time).isEqualTo("0:01") + assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause) + assertThat(it.progress).isEqualTo(0.1f) + assertThat(it.time).isEqualTo("0:01") } initialState.eventSink(VoiceMessageEvents.Seek(0.5f)) awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause) - Truth.assertThat(it.progress).isEqualTo(0.5f) - Truth.assertThat(it.time).isEqualTo("0:05") + assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause) + assertThat(it.progress).isEqualTo(0.5f) + assertThat(it.time).isEqualTo("0:05") } } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/factories/event/TimelineItemContentPollFactoryTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/factories/event/TimelineItemContentPollFactoryTest.kt deleted file mode 100644 index 8cf5704bba..0000000000 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/factories/event/TimelineItemContentPollFactoryTest.kt +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.features.messages.timeline.factories.event - -import com.google.common.truth.Truth -import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentPollFactory -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent -import io.element.android.features.poll.api.PollAnswerItem -import io.element.android.libraries.featureflag.api.FeatureFlags -import io.element.android.libraries.featureflag.test.FakeFeatureFlagService -import io.element.android.libraries.matrix.api.core.EventId -import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.api.poll.PollAnswer -import io.element.android.libraries.matrix.api.poll.PollKind -import io.element.android.libraries.matrix.api.timeline.item.event.PollContent -import io.element.android.libraries.matrix.test.AN_EVENT_ID -import io.element.android.libraries.matrix.test.A_USER_ID -import io.element.android.libraries.matrix.test.A_USER_ID_10 -import io.element.android.libraries.matrix.test.A_USER_ID_2 -import io.element.android.libraries.matrix.test.A_USER_ID_3 -import io.element.android.libraries.matrix.test.A_USER_ID_4 -import io.element.android.libraries.matrix.test.A_USER_ID_5 -import io.element.android.libraries.matrix.test.A_USER_ID_6 -import io.element.android.libraries.matrix.test.A_USER_ID_7 -import io.element.android.libraries.matrix.test.A_USER_ID_8 -import io.element.android.libraries.matrix.test.A_USER_ID_9 -import io.element.android.libraries.matrix.test.FakeMatrixClient -import kotlinx.coroutines.test.runTest -import org.junit.Test - -internal class TimelineItemContentPollFactoryTest { - - private val factory = TimelineItemContentPollFactory( - matrixClient = FakeMatrixClient(), - featureFlagService = FakeFeatureFlagService(mapOf(FeatureFlags.Polls.key to true)), - ) - - @Test - fun `Disclosed poll - not ended, no votes`() = runTest { - Truth.assertThat(factory.create(aPollContent(), eventId = null)).isEqualTo(aTimelineItemPollContent()) - } - - @Test - fun `Disclosed poll - not ended, some votes, including one from current user`() = runTest { - val votes = MY_USER_WINNING_VOTES.mapKeys { it.key.id } - Truth.assertThat( - factory.create(aPollContent(votes = votes), eventId = null) - ) - .isEqualTo( - aTimelineItemPollContent( - answerItems = listOf( - aPollAnswerItem(answer = A_POLL_ANSWER_1, votesCount = 3, percentage = 0.3f), - aPollAnswerItem(answer = A_POLL_ANSWER_2, isSelected = true, votesCount = 6, percentage = 0.6f), - aPollAnswerItem(answer = A_POLL_ANSWER_3), - aPollAnswerItem(answer = A_POLL_ANSWER_4, votesCount = 1, percentage = 0.1f), - ), - ) - ) - } - - @Test - fun `Disclosed poll - ended, no votes, no winner`() = runTest { - Truth.assertThat( - factory.create(aPollContent(endTime = 1UL), eventId = null) - ).isEqualTo( - aTimelineItemPollContent().let { - it.copy( - answerItems = it.answerItems.map { answerItem -> answerItem.copy(isEnabled = false) }, - isEnded = true, - ) - } - ) - } - - @Test - fun `Disclosed poll - ended, some votes, including one from current user (winner)`() = runTest { - val votes = MY_USER_WINNING_VOTES.mapKeys { it.key.id } - Truth.assertThat( - factory.create(aPollContent(votes = votes, endTime = 1UL), eventId = null) - ) - .isEqualTo( - aTimelineItemPollContent( - answerItems = listOf( - aPollAnswerItem(answer = A_POLL_ANSWER_1, isEnabled = false, votesCount = 3, percentage = 0.3f), - aPollAnswerItem(answer = A_POLL_ANSWER_2, isSelected = true, isEnabled = false, isWinner = true, votesCount = 6, percentage = 0.6f), - aPollAnswerItem(answer = A_POLL_ANSWER_3, isEnabled = false), - aPollAnswerItem(answer = A_POLL_ANSWER_4, isEnabled = false, votesCount = 1, percentage = 0.1f), - ), - isEnded = true, - ) - ) - } - - @Test - fun `Disclosed poll - ended, some votes, including one from current user (not winner) and two winning votes`() = runTest { - val votes = OTHER_WINNING_VOTES.mapKeys { it.key.id } - Truth.assertThat( - factory.create(aPollContent(votes = votes, endTime = 1UL), eventId = null) - ) - .isEqualTo( - aTimelineItemPollContent( - answerItems = listOf( - aPollAnswerItem(answer = A_POLL_ANSWER_1, isEnabled = false, isWinner = true, votesCount = 4, percentage = 0.4f), - aPollAnswerItem(answer = A_POLL_ANSWER_2, isSelected = true, isEnabled = false, votesCount = 2, percentage = 0.2f), - aPollAnswerItem(answer = A_POLL_ANSWER_3, isEnabled = false), - aPollAnswerItem(answer = A_POLL_ANSWER_4, isEnabled = false, isWinner = true, votesCount = 4, percentage = 0.4f), - ), - isEnded = true, - ) - ) - } - - @Test - fun `Undisclosed poll - not ended, no votes`() = runTest { - Truth.assertThat( - factory.create(aPollContent(PollKind.Undisclosed).copy(), eventId = null) - ).isEqualTo( - aTimelineItemPollContent(pollKind = PollKind.Undisclosed).let { - it.copy(answerItems = it.answerItems.map { answerItem -> answerItem.copy(isDisclosed = false) }) - } - ) - } - - @Test - fun `Undisclosed poll - not ended, some votes, including one from current user`() = runTest { - val votes = MY_USER_WINNING_VOTES.mapKeys { it.key.id } - Truth.assertThat( - factory.create(aPollContent(pollKind = PollKind.Undisclosed, votes = votes), eventId = null) - ) - .isEqualTo( - aTimelineItemPollContent( - pollKind = PollKind.Undisclosed, - answerItems = listOf( - aPollAnswerItem(answer = A_POLL_ANSWER_1, isDisclosed = false, votesCount = 3, percentage = 0.3f), - aPollAnswerItem(answer = A_POLL_ANSWER_2, isDisclosed = false, isSelected = true, votesCount = 6, percentage = 0.6f), - aPollAnswerItem(answer = A_POLL_ANSWER_3, isDisclosed = false), - aPollAnswerItem(answer = A_POLL_ANSWER_4, isDisclosed = false, votesCount = 1, percentage = 0.1f), - ), - ) - ) - } - - @Test - fun `Undisclosed poll - ended, no votes, no winner`() = runTest { - Truth.assertThat( - factory.create(aPollContent(pollKind = PollKind.Undisclosed, endTime = 1UL), eventId = null) - ).isEqualTo( - aTimelineItemPollContent().let { - it.copy( - pollKind = PollKind.Undisclosed, - answerItems = it.answerItems.map { answerItem -> - answerItem.copy(isDisclosed = true, isEnabled = false, isWinner = false) - }, - isEnded = true, - ) - } - ) - } - - @Test - fun `Undisclosed poll - ended, some votes, including one from current user (winner)`() = runTest { - val votes = MY_USER_WINNING_VOTES.mapKeys { it.key.id } - Truth.assertThat( - factory.create(aPollContent(pollKind = PollKind.Undisclosed, votes = votes, endTime = 1UL), eventId = null) - ) - .isEqualTo( - aTimelineItemPollContent( - pollKind = PollKind.Undisclosed, - answerItems = listOf( - aPollAnswerItem(answer = A_POLL_ANSWER_1, isEnabled = false, votesCount = 3, percentage = 0.3f), - aPollAnswerItem(answer = A_POLL_ANSWER_2, isSelected = true, isEnabled = false, isWinner = true, votesCount = 6, percentage = 0.6f), - aPollAnswerItem(answer = A_POLL_ANSWER_3, isEnabled = false), - aPollAnswerItem(answer = A_POLL_ANSWER_4, isEnabled = false, votesCount = 1, percentage = 0.1f), - ), - isEnded = true, - ) - ) - } - - @Test - fun `Undisclosed poll - ended, some votes, including one from current user (not winner) and two winning votes`() = runTest { - val votes = OTHER_WINNING_VOTES.mapKeys { it.key.id } - Truth.assertThat( - factory.create(aPollContent(PollKind.Undisclosed).copy(votes = votes, endTime = 1UL), eventId = null) - ) - .isEqualTo( - aTimelineItemPollContent( - pollKind = PollKind.Undisclosed, - answerItems = listOf( - aPollAnswerItem(A_POLL_ANSWER_1, isEnabled = false, isWinner = true, votesCount = 4, percentage = 0.4f), - aPollAnswerItem(A_POLL_ANSWER_2, isSelected = true, isEnabled = false, votesCount = 2, percentage = 0.2f), - aPollAnswerItem(A_POLL_ANSWER_3, isEnabled = false), - aPollAnswerItem(A_POLL_ANSWER_4, isEnabled = false, isWinner = true, votesCount = 4, percentage = 0.4f), - ), - isEnded = true, - ) - ) - } - - @Test - fun `eventId is populated`() = runTest { - Truth.assertThat(factory.create(aPollContent(), eventId = null)) - .isEqualTo(aTimelineItemPollContent(eventId = null)) - - Truth.assertThat(factory.create(aPollContent(), eventId = AN_EVENT_ID)) - .isEqualTo(aTimelineItemPollContent(eventId = AN_EVENT_ID)) - } - - private fun aPollContent( - pollKind: PollKind = PollKind.Disclosed, - votes: Map> = emptyMap(), - endTime: ULong? = null, - ): PollContent = PollContent( - question = A_POLL_QUESTION, - kind = pollKind, - maxSelections = 1UL, - answers = listOf(A_POLL_ANSWER_1, A_POLL_ANSWER_2, A_POLL_ANSWER_3, A_POLL_ANSWER_4), - votes = votes, - endTime = endTime, - ) - - private fun aTimelineItemPollContent( - eventId: EventId? = null, - pollKind: PollKind = PollKind.Disclosed, - answerItems: List = listOf( - aPollAnswerItem(A_POLL_ANSWER_1), - aPollAnswerItem(A_POLL_ANSWER_2), - aPollAnswerItem(A_POLL_ANSWER_3), - aPollAnswerItem(A_POLL_ANSWER_4), - ), - isEnded: Boolean = false, - ) = TimelineItemPollContent( - eventId = eventId, - question = A_POLL_QUESTION, - answerItems = answerItems, - pollKind = pollKind, - isEnded = isEnded, - ) - - private fun aPollAnswerItem( - answer: PollAnswer, - isSelected: Boolean = false, - isEnabled: Boolean = true, - isWinner: Boolean = false, - isDisclosed: Boolean = true, - votesCount: Int = 0, - percentage: Float = 0f, - ) = PollAnswerItem( - answer = answer, - isSelected = isSelected, - isEnabled = isEnabled, - isWinner = isWinner, - isDisclosed = isDisclosed, - votesCount = votesCount, - percentage = percentage, - ) - - private companion object TestData { - private const val A_POLL_QUESTION = "What is your favorite food?" - private val A_POLL_ANSWER_1 = PollAnswer("id_1", "Pizza") - private val A_POLL_ANSWER_2 = PollAnswer("id_2", "Pasta") - private val A_POLL_ANSWER_3 = PollAnswer("id_3", "French Fries") - private val A_POLL_ANSWER_4 = PollAnswer("id_4", "Hamburger") - - private val MY_USER_WINNING_VOTES = mapOf( - A_POLL_ANSWER_1 to listOf(A_USER_ID_2, A_USER_ID_3, A_USER_ID_4), - A_POLL_ANSWER_2 to listOf(A_USER_ID /* my vote */, A_USER_ID_5, A_USER_ID_6, A_USER_ID_7, A_USER_ID_8, A_USER_ID_9), // winner - A_POLL_ANSWER_3 to emptyList(), - A_POLL_ANSWER_4 to listOf(A_USER_ID_10), - ) - private val OTHER_WINNING_VOTES = mapOf( - A_POLL_ANSWER_1 to listOf(A_USER_ID_2, A_USER_ID_3, A_USER_ID_4, A_USER_ID_5), // winner - A_POLL_ANSWER_2 to listOf(A_USER_ID /* my vote */, A_USER_ID_6), - A_POLL_ANSWER_3 to emptyList(), - A_POLL_ANSWER_4 to listOf(A_USER_ID_7, A_USER_ID_8, A_USER_ID_9, A_USER_ID_10), // winner - ) - } -} diff --git a/features/messages/test/build.gradle.kts b/features/messages/test/build.gradle.kts index 27360e9567..cb864a9cac 100644 --- a/features/messages/test/build.gradle.kts +++ b/features/messages/test/build.gradle.kts @@ -15,7 +15,7 @@ */ plugins { - id("io.element.android-library") + id("io.element.android-compose-library") } android { @@ -24,4 +24,5 @@ android { dependencies { api(projects.features.messages.api) + implementation(projects.libraries.matrix.api) } diff --git a/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/timeline/FakeHtmlConverterProvider.kt b/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/timeline/FakeHtmlConverterProvider.kt new file mode 100644 index 0000000000..0f7802632a --- /dev/null +++ b/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/timeline/FakeHtmlConverterProvider.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.test.timeline + +import androidx.compose.runtime.Composable +import io.element.android.features.messages.api.timeline.HtmlConverterProvider +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.wysiwyg.utils.HtmlConverter + +class FakeHtmlConverterProvider( + private val transform: (String) -> CharSequence = { it }, +): HtmlConverterProvider { + + @Composable + override fun Update(currentUserId: UserId) = Unit + + override fun provide(): HtmlConverter { + return object : HtmlConverter { + override fun fromHtmlToSpans(html: String): CharSequence { + return transform(html) + } + } + } +} diff --git a/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/Indicator.kt b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/Indicator.kt index 0be8557bed..050fba78ba 100644 --- a/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/Indicator.kt +++ b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/Indicator.kt @@ -32,11 +32,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.text.toDp import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -54,7 +54,7 @@ internal fun Indicator( ) { val tint = MaterialTheme.colorScheme.primary Icon( - resourceId = CommonDrawables.ic_compound_offline, + imageVector = CompoundIcons.Offline, contentDescription = null, tint = tint, modifier = Modifier.size(16.sp.toDp()), diff --git a/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt b/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt index b6d663d2c5..a0d2e2ce7f 100644 --- a/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt +++ b/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt @@ -35,22 +35,22 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.atomic.atoms.ElementLogoAtom import io.element.android.libraries.designsystem.atomic.atoms.ElementLogoAtomSize import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule import io.element.android.libraries.designsystem.atomic.pages.OnBoardingPage -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.OutlinedButton import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings // Refs: @@ -140,7 +140,7 @@ private fun OnBoardingContent( onClick = onOpenDeveloperSettings, ) { Icon( - resourceId = CommonDrawables.ic_compound_settings_solid, + imageVector = CompoundIcons.SettingsSolid, contentDescription = stringResource(CommonStrings.common_settings) ) } diff --git a/features/onboarding/impl/src/main/res/values-hu/translations.xml b/features/onboarding/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..8acd47c566 --- /dev/null +++ b/features/onboarding/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,9 @@ + + + "Kézi bejelentkezés" + "Bejelentkezés QR-kóddal" + "Fiók létrehozása" + "Üdvözöljük a valaha volt leggyorsabb Elementben. Felturbózva, a sebesség és az egyszerűség érdekében." + "Üdvözli az %1$s. Felturbózva, a sebesség és az egyszerűség jegyében." + "Legyen elemében" + diff --git a/features/onboarding/impl/src/main/res/values-in/translations.xml b/features/onboarding/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..06946a3cd8 --- /dev/null +++ b/features/onboarding/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,9 @@ + + + "Masuk secara manual" + "Masuk dengan kode QR" + "Buat akun" + "Selamat datang di Element tercepat yang pernah ada. Berdaya besar untuk kecepatan dan kesederhanaan." + "Selamat datang di %1$s. Berdaya penuh, untuk kecepatan dan kesederhanaan." + "Berada di elemen Anda" + diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/extensions/isEmail.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/actions/EndPollAction.kt similarity index 73% rename from libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/extensions/isEmail.kt rename to features/poll/api/src/main/kotlin/io/element/android/features/poll/api/actions/EndPollAction.kt index b47033286d..22982dce97 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/extensions/isEmail.kt +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/actions/EndPollAction.kt @@ -14,11 +14,10 @@ * limitations under the License. */ -package io.element.android.libraries.androidutils.extensions +package io.element.android.features.poll.api.actions -import android.util.Patterns +import io.element.android.libraries.matrix.api.core.EventId -/** - * Check if a CharSequence is an email. - */ -fun CharSequence.isEmail() = Patterns.EMAIL_ADDRESS.matcher(this).matches() +interface EndPollAction { + suspend fun execute(pollStartId: EventId): Result +} diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/utils/Colors.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/actions/SendPollResponseAction.kt similarity index 72% rename from libraries/theme/src/main/kotlin/io/element/android/libraries/theme/utils/Colors.kt rename to features/poll/api/src/main/kotlin/io/element/android/features/poll/api/actions/SendPollResponseAction.kt index ceb711da0a..71cf476f59 100644 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/utils/Colors.kt +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/actions/SendPollResponseAction.kt @@ -14,13 +14,10 @@ * limitations under the License. */ -package io.element.android.libraries.theme.utils +package io.element.android.features.poll.api.actions -import androidx.compose.ui.graphics.Color +import io.element.android.libraries.matrix.api.core.EventId -/** - * Convert color to Human Readable Format. - */ -fun Color.toHrf(): String { - return "0x" + value.toString(16).take(8).uppercase() +interface SendPollResponseAction { + suspend fun execute(pollStartId: EventId, answerId: String): Result } diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/create/CreatePollEntryPoint.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/create/CreatePollEntryPoint.kt index abbb041374..0676584eca 100644 --- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/create/CreatePollEntryPoint.kt +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/create/CreatePollEntryPoint.kt @@ -21,5 +21,14 @@ import com.bumble.appyx.core.node.Node import io.element.android.libraries.architecture.FeatureEntryPoint interface CreatePollEntryPoint : FeatureEntryPoint { - fun createNode(parentNode: Node, buildContext: BuildContext): Node + data class Params( + val mode: CreatePollMode, + ) + + fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder + + interface NodeBuilder { + fun params(params: Params): NodeBuilder + fun build(): Node + } } diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/create/CreatePollMode.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/create/CreatePollMode.kt new file mode 100644 index 0000000000..36c9cf57df --- /dev/null +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/create/CreatePollMode.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.api.create + +import io.element.android.libraries.matrix.api.core.EventId + +sealed interface CreatePollMode { + data object NewPoll : CreatePollMode + data class EditPoll(val eventId: EventId) : CreatePollMode +} diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/history/PollHistoryEntryPoint.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/history/PollHistoryEntryPoint.kt new file mode 100644 index 0000000000..573ac0ee10 --- /dev/null +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/history/PollHistoryEntryPoint.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.api.history + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.libraries.architecture.FeatureEntryPoint + +interface PollHistoryEntryPoint : FeatureEntryPoint { + fun createNode(parentNode: Node, buildContext: BuildContext): Node +} diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerItem.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollAnswerItem.kt similarity index 96% rename from features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerItem.kt rename to features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollAnswerItem.kt index 1955701c5b..56dbaeb3ca 100644 --- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerItem.kt +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollAnswerItem.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.poll.api +package io.element.android.features.poll.api.pollcontent import io.element.android.libraries.matrix.api.poll.PollAnswer diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollAnswerView.kt similarity index 98% rename from features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt rename to features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollAnswerView.kt index 30c7829d70..a84ef502ca 100644 --- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollAnswerView.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.poll.api +package io.element.android.features.poll.api.pollcontent import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -33,6 +33,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon @@ -40,7 +41,6 @@ import io.element.android.libraries.designsystem.theme.components.LinearProgress import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.progressIndicatorTrackColor import io.element.android.libraries.designsystem.toEnabledColor -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonPlurals @Composable diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentState.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentState.kt new file mode 100644 index 0000000000..b97029a22c --- /dev/null +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentState.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.api.pollcontent + +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.poll.PollKind +import kotlinx.collections.immutable.ImmutableList + +/** + * UI model for a PollContent. + * @property eventId the event id of the poll. + * @property question the poll question. + * @property answerItems the list of answers. + * @property pollKind the kind of poll. + * @property isPollEditable whether the poll is editable. + * @property isPollEnded whether the poll is ended. + * @property isMine whether the poll has been created by me. + */ +data class PollContentState( + val eventId: EventId?, + val question: String, + val answerItems: ImmutableList, + val pollKind: PollKind, + val isPollEditable: Boolean, + val isPollEnded: Boolean, + val isMine: Boolean, +) diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentStateFactory.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentStateFactory.kt new file mode 100644 index 0000000000..7178a6c3ee --- /dev/null +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentStateFactory.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.api.pollcontent + +import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.PollContent + +interface PollContentStateFactory { + suspend fun create(event: EventTimelineItem, content: PollContent): PollContentState +} diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerViewProvider.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentStateFixtures.kt similarity index 66% rename from features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerViewProvider.kt rename to features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentStateFixtures.kt index 42f0c4f527..80c6a6a6c4 100644 --- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerViewProvider.kt +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentStateFixtures.kt @@ -14,11 +14,15 @@ * limitations under the License. */ -package io.element.android.features.poll.api +package io.element.android.features.poll.api.pollcontent import io.element.android.libraries.matrix.api.poll.PollAnswer +import io.element.android.libraries.matrix.api.poll.PollKind +import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf +fun aPollQuestion() = "What type of food should we have at the party?" + fun aPollAnswerItemList( hasVotes: Boolean = true, isEnded: Boolean = false, @@ -30,7 +34,7 @@ fun aPollAnswerItemList( isEnabled = !isEnded, isWinner = isEnded, votesCount = if (hasVotes) 5 else 0, - percentage = 0.5f + percentage = if (hasVotes) 0.5f else 0f ), aPollAnswerItem( answer = PollAnswer("option_2", "Chinese \uD83C\uDDE8\uD83C\uDDF3"), @@ -47,9 +51,14 @@ fun aPollAnswerItemList( isWinner = false, isSelected = true, votesCount = if (hasVotes) 1 else 0, - percentage = 0.1f + percentage = if (hasVotes) 0.1f else 0f + ), + aPollAnswerItem( + isDisclosed = isDisclosed, + isEnabled = !isEnded, + votesCount = if (hasVotes) 4 else 0, + percentage = if (hasVotes) 0.4f else 0f, ), - aPollAnswerItem(isDisclosed = isDisclosed, isEnabled = !isEnded), ) fun aPollAnswerItem( @@ -72,3 +81,25 @@ fun aPollAnswerItem( votesCount = votesCount, percentage = percentage ) + +fun aPollContentState( + isMine: Boolean = false, + isEnded: Boolean = false, + isDisclosed: Boolean = true, + hasVotes: Boolean = true, + question: String = aPollQuestion(), + pollKind: PollKind = PollKind.Disclosed, + answerItems: ImmutableList = aPollAnswerItemList( + isEnded = isEnded, + isDisclosed = isDisclosed, + hasVotes = hasVotes + ), +) = PollContentState( + eventId = null, + question = question, + answerItems = answerItems, + pollKind = pollKind, + isPollEditable = isMine && !isEnded, + isPollEnded = isEnded, + isMine = isMine, +) diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollContentView.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentView.kt similarity index 87% rename from features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollContentView.kt rename to features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentView.kt index bfcccf3b89..706fa6e402 100644 --- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollContentView.kt +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentView.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.poll.api +package io.element.android.features.poll.api.pollcontent import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -35,26 +35,50 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.poll.PollAnswer import io.element.android.libraries.matrix.api.poll.PollKind -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList +@Composable +fun PollContentView( + state: PollContentState, + onAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit, + onPollEdit: (pollStartId: EventId) -> Unit, + onPollEnd: (pollStartId: EventId) -> Unit, + modifier: Modifier = Modifier, +) { + PollContentView( + eventId = state.eventId, + question = state.question, + answerItems = state.answerItems, + pollKind = state.pollKind, + isPollEditable = state.isPollEditable, + isPollEnded = state.isPollEnded, + isMine = state.isMine, + onPollEdit = onPollEdit, + onAnswerSelected = onAnswerSelected, + onPollEnd = onPollEnd, + modifier = modifier, + ) +} + @Composable fun PollContentView( eventId: EventId?, question: String, answerItems: ImmutableList, pollKind: PollKind, + isPollEditable: Boolean, isPollEnded: Boolean, isMine: Boolean, onAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit, @@ -103,8 +127,8 @@ fun PollContentView( if (isMine) { CreatorView( - votesCount = 1, // TODO Polls: set to `votesCount` when edit poll screen is implemented. isPollEnded = isPollEnded, + isPollEditable = isPollEditable, onPollEdit = ::onPollEdit, onPollEnd = { showConfirmation = true }, modifier = Modifier.fillMaxWidth(), @@ -125,13 +149,13 @@ private fun PollTitle( ) { if (isPollEnded) { Icon( - resourceId = CommonDrawables.ic_poll_end, + imageVector = CompoundIcons.PollsEnd, contentDescription = stringResource(id = CommonStrings.a11y_poll_end), modifier = Modifier.size(22.dp) ) } else { Icon( - resourceId = CommonDrawables.ic_compound_polls, + imageVector = CompoundIcons.Polls, contentDescription = stringResource(id = CommonStrings.a11y_poll), modifier = Modifier.size(22.dp) ) @@ -197,26 +221,25 @@ private fun ColumnScope.UndisclosedPollBottomNotice( @Composable private fun CreatorView( - @Suppress("SameParameterValue") votesCount: Int, // TODO Polls: remove @Suppress when edit poll screen is implemented. isPollEnded: Boolean, + isPollEditable: Boolean, onPollEdit: () -> Unit, onPollEnd: () -> Unit, modifier: Modifier = Modifier ) { - if (!isPollEnded) { - if (votesCount == 0) { + when { + isPollEditable -> Button( text = stringResource(id = CommonStrings.action_edit_poll), onClick = onPollEdit, modifier = modifier, ) - } else { + !isPollEnded -> Button( text = stringResource(id = CommonStrings.action_end_poll), onClick = onPollEnd, modifier = modifier, ) - } } } @@ -229,6 +252,7 @@ internal fun PollContentUndisclosedPreview() = ElementPreview { answerItems = aPollAnswerItemList(isDisclosed = false), pollKind = PollKind.Undisclosed, isPollEnded = false, + isPollEditable = false, isMine = false, onAnswerSelected = { _, _ -> }, onPollEdit = {}, @@ -245,6 +269,7 @@ internal fun PollContentDisclosedPreview() = ElementPreview { answerItems = aPollAnswerItemList(), pollKind = PollKind.Disclosed, isPollEnded = false, + isPollEditable = false, isMine = false, onAnswerSelected = { _, _ -> }, onPollEdit = {}, @@ -261,6 +286,7 @@ internal fun PollContentEndedPreview() = ElementPreview { answerItems = aPollAnswerItemList(isEnded = true), pollKind = PollKind.Disclosed, isPollEnded = true, + isPollEditable = false, isMine = false, onAnswerSelected = { _, _ -> }, onPollEdit = {}, @@ -270,13 +296,14 @@ internal fun PollContentEndedPreview() = ElementPreview { @PreviewsDayNight @Composable -internal fun PollContentCreatorNoVotesPreview() = ElementPreview { +internal fun PollContentCreatorEditablePreview() = ElementPreview { PollContentView( eventId = EventId("\$anEventId"), question = "What type of food should we have at the party?", answerItems = aPollAnswerItemList(hasVotes = false, isEnded = false), pollKind = PollKind.Disclosed, isPollEnded = false, + isPollEditable = true, isMine = true, onAnswerSelected = { _, _ -> }, onPollEdit = {}, @@ -293,6 +320,7 @@ internal fun PollContentCreatorPreview() = ElementPreview { answerItems = aPollAnswerItemList(isEnded = false), pollKind = PollKind.Disclosed, isPollEnded = false, + isPollEditable = false, isMine = true, onAnswerSelected = { _, _ -> }, onPollEdit = {}, @@ -309,6 +337,7 @@ internal fun PollContentCreatorEndedPreview() = ElementPreview { answerItems = aPollAnswerItemList(isEnded = true), pollKind = PollKind.Disclosed, isPollEnded = true, + isPollEditable = false, isMine = true, onAnswerSelected = { _, _ -> }, onPollEdit = {}, diff --git a/features/poll/impl/build.gradle.kts b/features/poll/impl/build.gradle.kts index 5ff9025aae..bfa74d3684 100644 --- a/features/poll/impl/build.gradle.kts +++ b/features/poll/impl/build.gradle.kts @@ -40,6 +40,7 @@ dependencies { implementation(projects.libraries.designsystem) implementation(projects.services.analytics.api) implementation(projects.features.messages.api) + implementation(projects.libraries.dateformatter.api) implementation(projects.libraries.uiStrings) testImplementation(libs.test.junit) @@ -51,6 +52,8 @@ dependencies { testImplementation(projects.services.analytics.test) testImplementation(projects.features.messages.test) testImplementation(projects.tests.testutils) + testImplementation(projects.libraries.dateformatter.test) + testImplementation(projects.features.poll.test) ksp(libs.showkase.processor) } diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/PollConstants.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/PollConstants.kt new file mode 100644 index 0000000000..3e75bc2427 --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/PollConstants.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl + +internal object PollConstants { + const val MIN_ANSWERS = 2 + const val MAX_ANSWERS = 20 + const val MAX_ANSWER_LENGTH = 240 + const val MAX_SELECTIONS = 1 +} diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/actions/DefaultEndPollAction.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/actions/DefaultEndPollAction.kt new file mode 100644 index 0000000000..9959f1aecc --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/actions/DefaultEndPollAction.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.actions + +import com.squareup.anvil.annotations.ContributesBinding +import im.vector.app.features.analytics.plan.PollEnd +import io.element.android.features.poll.api.actions.EndPollAction +import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.services.analytics.api.AnalyticsService +import javax.inject.Inject + +@ContributesBinding(RoomScope::class) +class DefaultEndPollAction @Inject constructor( + private val room: MatrixRoom, + private val analyticsService: AnalyticsService, +) : EndPollAction { + + override suspend fun execute(pollStartId: EventId): Result { + return room.endPoll( + pollStartId = pollStartId, + text = "The poll with event id: $pollStartId has ended." + ).onSuccess { + analyticsService.capture(PollEnd()) + } + } +} diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/actions/DefaultSendPollResponseAction.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/actions/DefaultSendPollResponseAction.kt new file mode 100644 index 0000000000..d6688ecb27 --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/actions/DefaultSendPollResponseAction.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.actions + +import com.squareup.anvil.annotations.ContributesBinding +import im.vector.app.features.analytics.plan.PollVote +import io.element.android.features.poll.api.actions.SendPollResponseAction +import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.services.analytics.api.AnalyticsService +import javax.inject.Inject + +@ContributesBinding(RoomScope::class) +class DefaultSendPollResponseAction @Inject constructor( + private val room: MatrixRoom, + private val analyticsService: AnalyticsService, +) : SendPollResponseAction { + + override suspend fun execute(pollStartId: EventId, answerId: String): Result { + return room.sendPollResponse( + pollStartId = pollStartId, + answers = listOf(answerId), + ).onSuccess { + analyticsService.capture(PollVote()) + } + } +} + diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollEvents.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollEvents.kt index 1251e07696..5831a73850 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollEvents.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollEvents.kt @@ -19,7 +19,8 @@ package io.element.android.features.poll.impl.create import io.element.android.libraries.matrix.api.poll.PollKind sealed interface CreatePollEvents { - data object Create : CreatePollEvents + data object Save : CreatePollEvents + data class Delete(val confirmed: Boolean) : CreatePollEvents data class SetQuestion(val question: String) : CreatePollEvents data class SetAnswer(val index: Int, val text: String) : CreatePollEvents data object AddAnswer : CreatePollEvents diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollException.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollException.kt new file mode 100644 index 0000000000..20c10c4716 --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollException.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.create + +internal sealed class CreatePollException : Exception() { + data class GetPollFailed( + override val message: String?, override val cause: Throwable? + ) : CreatePollException() + + data class SavePollFailed( + override val message: String?, override val cause: Throwable? + ) : CreatePollException() +} diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollNode.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollNode.kt index 506c39c177..a38ed3bb88 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollNode.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollNode.kt @@ -26,6 +26,9 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.anvilannotations.ContributesNode +import io.element.android.features.poll.api.create.CreatePollMode +import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.RoomScope import io.element.android.services.analytics.api.AnalyticsService @@ -37,7 +40,11 @@ class CreatePollNode @AssistedInject constructor( analyticsService: AnalyticsService, ) : Node(buildContext, plugins = plugins) { - private val presenter = presenterFactory.create(backNavigator = ::navigateUp) + data class Inputs(val mode: CreatePollMode) : NodeInputs + + private val inputs: Inputs = inputs() + + private val presenter = presenterFactory.create(backNavigator = ::navigateUp, mode = inputs.mode) init { lifecycle.subscribe( diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenter.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenter.kt index 44cc54a100..484e92eb15 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenter.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenter.kt @@ -17,13 +17,12 @@ package io.element.android.features.poll.impl.create import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.Saver import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dagger.assisted.Assisted @@ -32,149 +31,216 @@ import dagger.assisted.AssistedInject import im.vector.app.features.analytics.plan.Composer import im.vector.app.features.analytics.plan.PollCreation import io.element.android.features.messages.api.MessageComposerContext +import io.element.android.features.poll.api.create.CreatePollMode +import io.element.android.features.poll.impl.PollConstants.MAX_SELECTIONS +import io.element.android.features.poll.impl.data.PollRepository import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.matrix.api.poll.PollAnswer import io.element.android.libraries.matrix.api.poll.PollKind -import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.poll.isDisclosed import io.element.android.services.analytics.api.AnalyticsService import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.launch import timber.log.Timber -private const val MIN_ANSWERS = 2 -private const val MAX_ANSWERS = 20 -private const val MAX_ANSWER_LENGTH = 240 -private const val MAX_SELECTIONS = 1 - class CreatePollPresenter @AssistedInject constructor( - private val room: MatrixRoom, + private val repository: PollRepository, private val analyticsService: AnalyticsService, private val messageComposerContext: MessageComposerContext, @Assisted private val navigateUp: () -> Unit, + @Assisted private val mode: CreatePollMode, ) : Presenter { @AssistedFactory interface Factory { - fun create(backNavigator: () -> Unit): CreatePollPresenter + fun create(backNavigator: () -> Unit, mode: CreatePollMode): CreatePollPresenter } @Composable override fun present(): CreatePollState { + // The initial state of the form. In edit mode this will be populated with the poll being edited. + var initialPoll: PollFormState by rememberSaveable(stateSaver = pollFormStateSaver) { + mutableStateOf(PollFormState.Empty) + } + // The current state of the form. + var poll: PollFormState by rememberSaveable(stateSaver = pollFormStateSaver) { + mutableStateOf(initialPoll) + } - var question: String by rememberSaveable { mutableStateOf("") } - var answers: List by rememberSaveable() { mutableStateOf(listOf("", "")) } - var pollKind: PollKind by rememberSaveable(saver = pollKindSaver) { mutableStateOf(PollKind.Disclosed) } - var showConfirmation: Boolean by rememberSaveable { mutableStateOf(false) } + // Whether the form has been changed from the initial state + val isDirty: Boolean by remember { derivedStateOf { poll != initialPoll } } - val canCreate: Boolean by remember { derivedStateOf { canCreate(question, answers) } } - val canAddAnswer: Boolean by remember { derivedStateOf { canAddAnswer(answers) } } - val immutableAnswers: ImmutableList by remember { derivedStateOf { answers.toAnswers() } } + var showBackConfirmation: Boolean by rememberSaveable { mutableStateOf(false) } + var showDeleteConfirmation: Boolean by rememberSaveable { mutableStateOf(false) } + + LaunchedEffect(Unit) { + if (mode is CreatePollMode.EditPoll) { + repository.getPoll(mode.eventId).onSuccess { + val loadedPoll = PollFormState( + question = it.question, + answers = it.answers.map(PollAnswer::text).toPersistentList(), + isDisclosed = it.kind.isDisclosed, + ) + initialPoll = loadedPoll + poll = loadedPoll + }.onFailure { + analyticsService.trackGetPollFailed(it) + navigateUp() + } + } + } + + val canSave: Boolean by remember { derivedStateOf { poll.isValid } } + val canAddAnswer: Boolean by remember { derivedStateOf { poll.canAddAnswer } } + val immutableAnswers: ImmutableList by remember { derivedStateOf { poll.toUiAnswers() } } val scope = rememberCoroutineScope() fun handleEvents(event: CreatePollEvents) { when (event) { - is CreatePollEvents.Create -> scope.launch { - if (canCreate) { - room.createPoll( - question = question, - answers = answers, + is CreatePollEvents.Save -> scope.launch { + if (canSave) { + repository.savePoll( + existingPollId = when (mode) { + is CreatePollMode.EditPoll -> mode.eventId + is CreatePollMode.NewPoll -> null + }, + question = poll.question, + answers = poll.answers, + pollKind = poll.pollKind, maxSelections = MAX_SELECTIONS, - pollKind = pollKind, - ) - analyticsService.capture( - Composer( - inThread = messageComposerContext.composerMode.inThread, - isEditing = messageComposerContext.composerMode.isEditing, - isReply = messageComposerContext.composerMode.isReply, - messageType = Composer.MessageType.Poll, + ).onSuccess { + analyticsService.capturePollSaved( + isUndisclosed = poll.pollKind == PollKind.Undisclosed, + numberOfAnswers = poll.answers.size, ) - ) - analyticsService.capture( - PollCreation( - action = PollCreation.Action.Create, - isUndisclosed = pollKind == PollKind.Undisclosed, - numberOfAnswers = answers.size, - ) - ) + }.onFailure { + analyticsService.trackSavePollFailed(it, mode) + } navigateUp() } else { Timber.d("Cannot create poll") } } - is CreatePollEvents.AddAnswer -> { - answers = answers + "" - } - is CreatePollEvents.RemoveAnswer -> { - answers = answers.filterIndexed { index, _ -> index != event.index } - } - is CreatePollEvents.SetAnswer -> { - answers = answers.toMutableList().apply { - this[event.index] = event.text.take(MAX_ANSWER_LENGTH) + is CreatePollEvents.Delete -> { + if (mode !is CreatePollMode.EditPoll) { + return + } + + if (!event.confirmed) { + showDeleteConfirmation = true + return + } + + scope.launch { + showDeleteConfirmation = false + repository.deletePoll(mode.eventId) + navigateUp() } } + is CreatePollEvents.AddAnswer -> { + poll = poll.withNewAnswer() + } + is CreatePollEvents.RemoveAnswer -> { + poll= poll.withAnswerRemoved(event.index) + } + is CreatePollEvents.SetAnswer -> { + poll = poll.withAnswerChanged(event.index, event.text) + } is CreatePollEvents.SetPollKind -> { - pollKind = event.pollKind + poll = poll.copy(isDisclosed = event.pollKind.isDisclosed) } is CreatePollEvents.SetQuestion -> { - question = event.question + poll = poll.copy(question = event.question) } is CreatePollEvents.NavBack -> { navigateUp() } CreatePollEvents.ConfirmNavBack -> { - val shouldConfirm = question.isNotBlank() || answers.any { it.isNotBlank() } + val shouldConfirm = isDirty if (shouldConfirm) { - showConfirmation = true + showBackConfirmation = true } else { navigateUp() } } - is CreatePollEvents.HideConfirmation -> showConfirmation = false + is CreatePollEvents.HideConfirmation -> { + showBackConfirmation = false + showDeleteConfirmation = false + } } } return CreatePollState( - canCreate = canCreate, + mode = when (mode) { + is CreatePollMode.NewPoll -> CreatePollState.Mode.New + is CreatePollMode.EditPoll -> CreatePollState.Mode.Edit + }, + canSave = canSave, canAddAnswer = canAddAnswer, - question = question, + question = poll.question, answers = immutableAnswers, - pollKind = pollKind, - showConfirmation = showConfirmation, + pollKind = poll.pollKind, + showBackConfirmation = showBackConfirmation, + showDeleteConfirmation = showDeleteConfirmation, eventSink = ::handleEvents, ) } + + private fun AnalyticsService.capturePollSaved( + isUndisclosed: Boolean, + numberOfAnswers: Int, + ) { + capture( + Composer( + inThread = messageComposerContext.composerMode.inThread, + isEditing = mode is CreatePollMode.EditPoll, + isReply = messageComposerContext.composerMode.isReply, + messageType = Composer.MessageType.Poll, + ) + ) + capture( + PollCreation( + action = when (mode) { + is CreatePollMode.EditPoll -> PollCreation.Action.Edit + is CreatePollMode.NewPoll -> PollCreation.Action.Create + }, + isUndisclosed = isUndisclosed, + numberOfAnswers = numberOfAnswers, + ) + ) + } } -private fun canCreate( - question: String, - answers: List -) = question.isNotBlank() && answers.size >= MIN_ANSWERS && answers.all { it.isNotBlank() } +private fun AnalyticsService.trackGetPollFailed(cause: Throwable) { + val exception = CreatePollException.GetPollFailed( + message = "Tried to edit poll but couldn't get poll", + cause = cause, + ) + Timber.e(exception) + trackError(exception) +} -private fun canAddAnswer(answers: List) = answers.size < MAX_ANSWERS +private fun AnalyticsService.trackSavePollFailed(cause: Throwable, mode: CreatePollMode) { + val exception = CreatePollException.SavePollFailed( + message = when (mode) { + CreatePollMode.NewPoll -> "Failed to create poll" + is CreatePollMode.EditPoll -> "Failed to edit poll" + }, + cause = cause, + ) + Timber.e(exception) + trackError(exception) +} -private fun List.toAnswers(): ImmutableList { - return map { answer -> +fun PollFormState.toUiAnswers(): ImmutableList { + return answers.map { answer -> Answer( text = answer, - canDelete = this.size > MIN_ANSWERS, + canDelete = canDeleteAnswer, ) }.toImmutableList() } -private val pollKindSaver: Saver, Boolean> = Saver( - save = { - when (it.value) { - PollKind.Disclosed -> false - PollKind.Undisclosed -> true - } - }, - restore = { - mutableStateOf( - when (it) { - true -> PollKind.Undisclosed - else -> PollKind.Disclosed - } - ) - } -) diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollState.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollState.kt index 0a7a9ad618..d3248be8a5 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollState.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollState.kt @@ -20,14 +20,23 @@ import io.element.android.libraries.matrix.api.poll.PollKind import kotlinx.collections.immutable.ImmutableList data class CreatePollState( - val canCreate: Boolean, + val mode: Mode, + val canSave: Boolean, val canAddAnswer: Boolean, val question: String, val answers: ImmutableList, val pollKind: PollKind, - val showConfirmation: Boolean, + val showBackConfirmation: Boolean, + val showDeleteConfirmation: Boolean, val eventSink: (CreatePollEvents) -> Unit, -) +) { + enum class Mode { + New, + Edit, + } + + val canDelete: Boolean = mode == Mode.Edit +} data class Answer( val text: String, diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollStateProvider.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollStateProvider.kt index c1393e0da0..d6d843eff9 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollStateProvider.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollStateProvider.kt @@ -25,6 +25,7 @@ class CreatePollStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aCreatePollState( + mode = CreatePollState.Mode.New, canCreate = false, canAddAnswer = true, question = "", @@ -33,9 +34,11 @@ class CreatePollStateProvider : PreviewParameterProvider { Answer("", false) ), pollKind = PollKind.Disclosed, - showConfirmation = false, + showBackConfirmation = false, + showDeleteConfirmation = false, ), aCreatePollState( + mode = CreatePollState.Mode.New, canCreate = true, canAddAnswer = true, question = "What type of food should we have?", @@ -43,10 +46,12 @@ class CreatePollStateProvider : PreviewParameterProvider { Answer("Italian \uD83C\uDDEE\uD83C\uDDF9", false), Answer("Chinese \uD83C\uDDE8\uD83C\uDDF3", false), ), - showConfirmation = false, + showBackConfirmation = false, + showDeleteConfirmation = false, pollKind = PollKind.Undisclosed, ), aCreatePollState( + mode = CreatePollState.Mode.New, canCreate = true, canAddAnswer = true, question = "What type of food should we have?", @@ -54,10 +59,12 @@ class CreatePollStateProvider : PreviewParameterProvider { Answer("Italian \uD83C\uDDEE\uD83C\uDDF9", false), Answer("Chinese \uD83C\uDDE8\uD83C\uDDF3", false), ), - showConfirmation = true, + showBackConfirmation = true, + showDeleteConfirmation = false, pollKind = PollKind.Undisclosed, ), aCreatePollState( + mode = CreatePollState.Mode.New, canCreate = true, canAddAnswer = true, question = "What type of food should we have?", @@ -67,10 +74,12 @@ class CreatePollStateProvider : PreviewParameterProvider { Answer("Brazilian \uD83C\uDDE7\uD83C\uDDF7", true), Answer("French \uD83C\uDDEB\uD83C\uDDF7", true), ), - showConfirmation = false, + showBackConfirmation = false, + showDeleteConfirmation = false, pollKind = PollKind.Undisclosed, ), aCreatePollState( + mode = CreatePollState.Mode.New, canCreate = true, canAddAnswer = false, question = "Should there be more than 20 answers?", @@ -96,10 +105,12 @@ class CreatePollStateProvider : PreviewParameterProvider { Answer("19", true), Answer("20", true), ), - showConfirmation = false, + showBackConfirmation = false, + showDeleteConfirmation = false, pollKind = PollKind.Undisclosed, ), aCreatePollState( + mode = CreatePollState.Mode.New, canCreate = true, canAddAnswer = true, question = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + @@ -118,26 +129,57 @@ class CreatePollStateProvider : PreviewParameterProvider { false ), ), - showConfirmation = false, + showBackConfirmation = false, + showDeleteConfirmation = false, pollKind = PollKind.Undisclosed, - ) + ), + aCreatePollState( + mode = CreatePollState.Mode.Edit, + canCreate = false, + canAddAnswer = true, + question = "", + answers = persistentListOf( + Answer("", false), + Answer("", false) + ), + pollKind = PollKind.Disclosed, + showDeleteConfirmation = false, + showBackConfirmation = false, + ), + aCreatePollState( + mode = CreatePollState.Mode.Edit, + canCreate = false, + canAddAnswer = true, + question = "", + answers = persistentListOf( + Answer("", false), + Answer("", false) + ), + pollKind = PollKind.Disclosed, + showDeleteConfirmation = true, + showBackConfirmation = false, + ), ) } private fun aCreatePollState( + mode: CreatePollState.Mode, canCreate: Boolean, canAddAnswer: Boolean, question: String, answers: PersistentList, - showConfirmation: Boolean, + showBackConfirmation: Boolean, + showDeleteConfirmation: Boolean, pollKind: PollKind ): CreatePollState { return CreatePollState( - canCreate = canCreate, + mode = mode, + canSave = canCreate, canAddAnswer = canAddAnswer, question = question, answers = answers, - showConfirmation = showConfirmation, + showBackConfirmation = showBackConfirmation, + showDeleteConfirmation = showDeleteConfirmation, pollKind = pollKind, eventSink = {} ) diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt index b79054ba84..fbea82608a 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt @@ -43,12 +43,14 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.poll.impl.R import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.components.list.ListItemContent -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.Icon @@ -60,14 +62,11 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.poll.PollKind -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -@OptIn(ExperimentalMaterial3Api::class) @Composable fun CreatePollView( state: CreatePollState, @@ -77,11 +76,21 @@ fun CreatePollView( val navBack = { state.eventSink(CreatePollEvents.ConfirmNavBack) } BackHandler(onBack = navBack) - if (state.showConfirmation) ConfirmationDialog( - content = stringResource(id = R.string.screen_create_poll_discard_confirmation), - onSubmitClicked = { state.eventSink(CreatePollEvents.NavBack) }, - onDismiss = { state.eventSink(CreatePollEvents.HideConfirmation) } - ) + if (state.showBackConfirmation) { + ConfirmationDialog( + content = stringResource(id = R.string.screen_create_poll_cancel_confirmation_content_android), + onSubmitClicked = { state.eventSink(CreatePollEvents.NavBack) }, + onDismiss = { state.eventSink(CreatePollEvents.HideConfirmation) } + ) + } + if (state.showDeleteConfirmation) { + ConfirmationDialog( + title = stringResource(id = R.string.screen_edit_poll_delete_confirmation_title), + content = stringResource(id = R.string.screen_edit_poll_delete_confirmation), + onSubmitClicked = { state.eventSink(CreatePollEvents.Delete(confirmed = true)) }, + onDismiss = { state.eventSink(CreatePollEvents.HideConfirmation) } + ) + } val questionFocusRequester = remember { FocusRequester() } val answerFocusRequester = remember { FocusRequester() } LaunchedEffect(Unit) { @@ -90,23 +99,11 @@ fun CreatePollView( Scaffold( modifier = modifier, topBar = { - TopAppBar( - title = { - Text( - text = stringResource(id = R.string.screen_create_poll_title), - style = ElementTheme.typography.aliasScreenTitle, - ) - }, - navigationIcon = { - BackButton(onClick = navBack) - }, - actions = { - TextButton( - text = stringResource(id = CommonStrings.action_create), - onClick = { state.eventSink(CreatePollEvents.Create) }, - enabled = state.canCreate, - ) - } + CreatePollTopAppBar( + mode = state.mode, + saveEnabled = state.canSave, + onBackPress = navBack, + onSaveClicked = { state.eventSink(CreatePollEvents.Save) } ) }, ) { paddingValues -> @@ -165,7 +162,7 @@ fun CreatePollView( }, trailingContent = ListItemContent.Custom { Icon( - resourceId = CommonDrawables.ic_compound_delete, + imageVector = CompoundIcons.Delete, contentDescription = null, modifier = Modifier.clickable(answer.canDelete) { state.eventSink(CreatePollEvents.RemoveAnswer(index)) @@ -204,12 +201,53 @@ fun CreatePollView( onChange = { state.eventSink(CreatePollEvents.SetPollKind(if (it) PollKind.Undisclosed else PollKind.Disclosed)) }, ), ) + if (state.canDelete) { + ListItem( + headlineContent = { Text(text = stringResource(id = CommonStrings.action_delete_poll)) }, + style = ListItemStyle.Destructive, + onClick = { state.eventSink(CreatePollEvents.Delete(confirmed = false)) }, + ) + } } } } } } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun CreatePollTopAppBar( + mode: CreatePollState.Mode, + saveEnabled: Boolean, + onBackPress: () -> Unit = {}, + onSaveClicked: () -> Unit = {}, +) { + TopAppBar( + title = { + Text( + text = when (mode) { + CreatePollState.Mode.New -> stringResource(id = R.string.screen_create_poll_title) + CreatePollState.Mode.Edit -> stringResource(id = R.string.screen_edit_poll_title) + }, + style = ElementTheme.typography.aliasScreenTitle, + ) + }, + navigationIcon = { + BackButton(onClick = onBackPress) + }, + actions = { + TextButton( + text = when (mode) { + CreatePollState.Mode.New -> stringResource(id = CommonStrings.action_create) + CreatePollState.Mode.Edit -> stringResource(id = CommonStrings.action_done) + }, + onClick = onSaveClicked, + enabled = saveEnabled, + ) + } + ) +} + @PreviewsDayNight @Composable internal fun CreatePollViewPreview( diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/DefaultCreatePollEntryPoint.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/DefaultCreatePollEntryPoint.kt index 1ce64deb88..713fe8effc 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/DefaultCreatePollEntryPoint.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/DefaultCreatePollEntryPoint.kt @@ -18,6 +18,7 @@ package io.element.android.features.poll.impl.create import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin import com.squareup.anvil.annotations.ContributesBinding import io.element.android.features.poll.api.create.CreatePollEntryPoint import io.element.android.libraries.architecture.createNode @@ -26,7 +27,19 @@ import javax.inject.Inject @ContributesBinding(AppScope::class) class DefaultCreatePollEntryPoint @Inject constructor() : CreatePollEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext): Node { - return parentNode.createNode(buildContext) + override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): CreatePollEntryPoint.NodeBuilder { + val plugins = ArrayList() + + return object : CreatePollEntryPoint.NodeBuilder { + + override fun params(params: CreatePollEntryPoint.Params): CreatePollEntryPoint.NodeBuilder { + plugins += CreatePollNode.Inputs(mode = params.mode) + return this + } + + override fun build(): Node { + return parentNode.createNode(buildContext, plugins) + } + } } } diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/PollFormState.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/PollFormState.kt new file mode 100644 index 0000000000..461b96b018 --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/PollFormState.kt @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.create + +import androidx.compose.runtime.saveable.Saver +import androidx.compose.runtime.saveable.mapSaver +import io.element.android.features.poll.impl.PollConstants +import io.element.android.features.poll.impl.PollConstants.MIN_ANSWERS +import io.element.android.libraries.matrix.api.poll.PollKind +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toPersistentList + +/** + * Represents the state of the poll creation / edit form. + * + * Save this state using [pollFormStateSaver]. + */ +data class PollFormState( + val question: String, + val answers: ImmutableList, + val isDisclosed: Boolean, +) { + companion object { + val Empty = PollFormState( + question = "", + answers = MutableList(MIN_ANSWERS) { "" }.toPersistentList(), + isDisclosed = true, + ) + } + + val pollKind + get() = when (isDisclosed) { + true -> PollKind.Disclosed + false -> PollKind.Undisclosed + } + + /** + * Create a copy of the [PollFormState] with a new blank answer added. + * + * If the maximum number of answers has already been reached an answer is not added. + */ + fun withNewAnswer(): PollFormState { + if (!canAddAnswer) { + return this + } + + return copy(answers = (answers + "").toPersistentList()) + } + + /** + * Create a copy of the [PollFormState] with the answer at [index] removed. + * + * If the answer doesn't exist or can't be removed, the state is unchanged. + * + * @param index the index of the answer to remove. + * + * @return a new [PollFormState] with the answer at [index] removed. + */ + fun withAnswerRemoved(index: Int): PollFormState { + if (!canDeleteAnswer) { + return this + } + + return copy(answers = answers.filterIndexed { i, _ -> i != index }.toPersistentList()) + } + + /** + * Create a copy of the [PollFormState] with the answer at [index] changed. + * + * If the new answer is longer than [PollConstants.MAX_ANSWER_LENGTH], it will be truncated. + * + * @param index the index of the answer to change. + * @param rawAnswer the new answer as the user typed it. + * + * @return a new [PollFormState] with the answer at [index] changed. + */ + fun withAnswerChanged(index: Int, rawAnswer: String): PollFormState = + copy(answers = answers.toMutableList().apply { + this[index] = rawAnswer.take(PollConstants.MAX_ANSWER_LENGTH) + }.toPersistentList()) + + /** + * Whether a new answer can be added. + */ + val canAddAnswer get() = answers.size < PollConstants.MAX_ANSWERS + + /** + * Whether any answer can be deleted. + */ + val canDeleteAnswer get() = answers.size > MIN_ANSWERS + + /** + * Whether the form is currently valid. + */ + val isValid get() = question.isNotBlank() && answers.size >= MIN_ANSWERS && answers.all { it.isNotBlank() } +} + +/** + * A [Saver] for [PollFormState]. + */ +internal val pollFormStateSaver = mapSaver( + save = { + mutableMapOf( + "question" to it.question, + "answers" to it.answers.toTypedArray(), + "isDisclosed" to it.isDisclosed, + ) + }, + restore = { saved -> + PollFormState( + question = saved["question"] as String, + answers = (saved["answers"] as Array<*>).map { it as String }.toPersistentList(), + isDisclosed = saved["isDisclosed"] as Boolean, + ) + } +) diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/data/PollRepository.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/data/PollRepository.kt new file mode 100644 index 0000000000..0b9aa5aee0 --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/data/PollRepository.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.data + +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.poll.PollKind +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +import kotlinx.coroutines.flow.first +import javax.inject.Inject + +class PollRepository @Inject constructor( + private val room: MatrixRoom, +) { + suspend fun getPoll(eventId: EventId): Result = runCatching { + room.timeline + .timelineItems + .first() + .asSequence() + .filterIsInstance() + .first { it.eventId == eventId } + .event + .content as PollContent + } + + suspend fun savePoll( + existingPollId: EventId?, + question: String, + answers: List, + pollKind: PollKind, + maxSelections: Int, + ): Result = when (existingPollId) { + null -> room.createPoll( + question = question, + answers = answers, + maxSelections = maxSelections, + pollKind = pollKind, + ) + else -> room.editPoll( + pollStartId = existingPollId, + question = question, + answers = answers, + maxSelections = maxSelections, + pollKind = pollKind, + ) + } + + suspend fun deletePoll( + pollStartId: EventId, + ): Result = + room.redactEvent( + eventId = pollStartId, + ) +} diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/DefaultPollHistoryEntryPoint.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/DefaultPollHistoryEntryPoint.kt new file mode 100644 index 0000000000..f517c8db06 --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/DefaultPollHistoryEntryPoint.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.history + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.poll.api.history.PollHistoryEntryPoint +import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.di.AppScope +import javax.inject.Inject + +@ContributesBinding(AppScope::class) +class DefaultPollHistoryEntryPoint @Inject constructor() : PollHistoryEntryPoint { + override fun createNode(parentNode: Node, buildContext: BuildContext): Node { + return parentNode.createNode(buildContext) + } +} diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryEvents.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryEvents.kt new file mode 100644 index 0000000000..edc848da41 --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryEvents.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.history + +import io.element.android.features.poll.impl.history.model.PollHistoryFilter +import io.element.android.libraries.matrix.api.core.EventId + +sealed interface PollHistoryEvents { + data object LoadMore : PollHistoryEvents + data class PollAnswerSelected(val pollStartId: EventId, val answerId: String) : PollHistoryEvents + data class PollEndClicked(val pollStartId: EventId) : PollHistoryEvents + data class OnFilterSelected(val filter: PollHistoryFilter) : PollHistoryEvents +} diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryFlowNode.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryFlowNode.kt new file mode 100644 index 0000000000..918f6c7abb --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryFlowNode.kt @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.history + +import android.os.Parcelable +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import com.bumble.appyx.navmodel.backstack.BackStack +import com.bumble.appyx.navmodel.backstack.operation.push +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import io.element.android.anvilannotations.ContributesNode +import io.element.android.features.poll.api.create.CreatePollEntryPoint +import io.element.android.features.poll.api.create.CreatePollMode +import io.element.android.libraries.architecture.BackstackView +import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.matrix.api.core.EventId +import kotlinx.parcelize.Parcelize + +@ContributesNode(RoomScope::class) +class PollHistoryFlowNode @AssistedInject constructor( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, + private val createPollEntryPoint: CreatePollEntryPoint, +) : BaseFlowNode( + backstack = BackStack( + initialElement = NavTarget.Root, + savedStateMap = buildContext.savedStateMap, + ), + buildContext = buildContext, + plugins = plugins +) { + + sealed interface NavTarget : Parcelable { + @Parcelize + data object Root : NavTarget + + @Parcelize + data class EditPoll(val pollStartEventId: EventId) : NavTarget + } + + override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { + return when (navTarget) { + is NavTarget.EditPoll -> { + createPollEntryPoint.nodeBuilder(this, buildContext) + .params(CreatePollEntryPoint.Params(mode = CreatePollMode.EditPoll(eventId = navTarget.pollStartEventId))) + .build() + } + NavTarget.Root -> { + val callback = object : PollHistoryNode.Callback { + override fun onEditPoll(pollStartEventId: EventId) { + backstack.push(NavTarget.EditPoll(pollStartEventId)) + } + } + createNode( + buildContext = buildContext, + plugins = listOf(callback) + ) + } + } + } + + @Composable + override fun View(modifier: Modifier) { + BackstackView() + } + +} diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryNode.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryNode.kt new file mode 100644 index 0000000000..7e6ba37743 --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryNode.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.history + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import com.bumble.appyx.core.plugin.plugins +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import io.element.android.anvilannotations.ContributesNode +import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.matrix.api.core.EventId + +@ContributesNode(RoomScope::class) +class PollHistoryNode @AssistedInject constructor( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, + private val presenter: PollHistoryPresenter, +) : Node( + buildContext = buildContext, + plugins = plugins, +) { + + interface Callback : Plugin { + fun onEditPoll(pollStartEventId: EventId) + } + + private fun onEditPoll(pollStartEventId: EventId) { + plugins().forEach { it.onEditPoll(pollStartEventId) } + } + + @Composable + override fun View(modifier: Modifier) { + PollHistoryView( + state = presenter.present(), + modifier = modifier, + onEditPoll = ::onEditPoll, + goBack = this::navigateUp, + ) + } +} diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryPresenter.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryPresenter.kt new file mode 100644 index 0000000000..784f64c416 --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryPresenter.kt @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.history + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import io.element.android.features.poll.api.actions.EndPollAction +import io.element.android.features.poll.api.actions.SendPollResponseAction +import io.element.android.features.poll.impl.history.model.PollHistoryFilter +import io.element.android.features.poll.impl.history.model.PollHistoryItems +import io.element.android.features.poll.impl.history.model.PollHistoryItemsFactory +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.timeline.MatrixTimeline +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import javax.inject.Inject + +class PollHistoryPresenter @Inject constructor( + private val room: MatrixRoom, + private val appCoroutineScope: CoroutineScope, + private val sendPollResponseAction: SendPollResponseAction, + private val endPollAction: EndPollAction, + private val pollHistoryItemFactory: PollHistoryItemsFactory, +) : Presenter { + + @Composable + override fun present(): PollHistoryState { + // TODO use room.rememberPollHistory() when working properly? + val timeline = room.timeline + val paginationState by timeline.paginationState.collectAsState() + val pollHistoryItemsFlow = remember { + timeline.timelineItems.map { items -> + pollHistoryItemFactory.create(items) + } + } + var activeFilter by rememberSaveable { + mutableStateOf(PollHistoryFilter.ONGOING) + } + val pollHistoryItems by pollHistoryItemsFlow.collectAsState(initial = PollHistoryItems()) + LaunchedEffect(paginationState, pollHistoryItems.size) { + if (pollHistoryItems.size == 0 && paginationState.canBackPaginate) loadMore(timeline) + } + val isLoading by remember { + derivedStateOf { + pollHistoryItems.size == 0 || paginationState.isBackPaginating + } + } + val coroutineScope = rememberCoroutineScope() + fun handleEvents(event: PollHistoryEvents) { + when (event) { + is PollHistoryEvents.LoadMore -> { + coroutineScope.loadMore(timeline) + } + is PollHistoryEvents.PollAnswerSelected -> appCoroutineScope.launch { + sendPollResponseAction.execute(pollStartId = event.pollStartId, answerId = event.answerId) + } + is PollHistoryEvents.PollEndClicked -> appCoroutineScope.launch { + endPollAction.execute(pollStartId = event.pollStartId) + } + is PollHistoryEvents.OnFilterSelected -> { + activeFilter = event.filter + } + } + } + + + return PollHistoryState( + isLoading = isLoading, + hasMoreToLoad = paginationState.hasMoreToLoadBackwards, + pollHistoryItems = pollHistoryItems, + activeFilter = activeFilter, + eventSink = ::handleEvents, + ) + } + + private fun CoroutineScope.loadMore(pollHistory: MatrixTimeline) = launch { + pollHistory.paginateBackwards(200) + } +} diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryState.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryState.kt new file mode 100644 index 0000000000..4d0f351bfd --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryState.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.history + +import io.element.android.features.poll.impl.history.model.PollHistoryFilter +import io.element.android.features.poll.impl.history.model.PollHistoryItem +import io.element.android.features.poll.impl.history.model.PollHistoryItems +import kotlinx.collections.immutable.ImmutableList + +data class PollHistoryState( + val isLoading: Boolean, + val hasMoreToLoad: Boolean, + val activeFilter: PollHistoryFilter, + val pollHistoryItems: PollHistoryItems, + val eventSink: (PollHistoryEvents) -> Unit, +) { + + fun pollHistoryForFilter(filter: PollHistoryFilter): ImmutableList { + return when (filter) { + PollHistoryFilter.ONGOING -> pollHistoryItems.ongoing + PollHistoryFilter.PAST -> pollHistoryItems.past + } + } +} diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryStateProvider.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryStateProvider.kt new file mode 100644 index 0000000000..9628a8e137 --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryStateProvider.kt @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.history + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.poll.api.pollcontent.PollContentState +import io.element.android.features.poll.api.pollcontent.aPollContentState +import io.element.android.features.poll.impl.history.model.PollHistoryFilter +import io.element.android.features.poll.impl.history.model.PollHistoryItem +import io.element.android.features.poll.impl.history.model.PollHistoryItems +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf + +class PollHistoryStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aPollHistoryState( + isLoading = false, + hasMoreToLoad = false, + activeFilter = PollHistoryFilter.ONGOING, + ), + aPollHistoryState( + isLoading = true, + hasMoreToLoad = true, + activeFilter = PollHistoryFilter.PAST, + ), + ) +} + +private fun aPollHistoryState( + isLoading: Boolean = false, + hasMoreToLoad: Boolean = false, + activeFilter: PollHistoryFilter = PollHistoryFilter.ONGOING, + currentItems: ImmutableList = persistentListOf( + aPollHistoryItem(), + ), +) = PollHistoryState( + isLoading = isLoading, + hasMoreToLoad = hasMoreToLoad, + activeFilter = activeFilter, + pollHistoryItems = PollHistoryItems( + ongoing = currentItems, + past = currentItems, + ), + eventSink = {}, +) + +private fun aPollHistoryItem( + formattedDate: String = "01/12/2023", + state: PollContentState = aPollContentState(), +) = PollHistoryItem( + formattedDate = formattedDate, + state = state, +) diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryView.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryView.kt new file mode 100644 index 0000000000..19ca239725 --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryView.kt @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.history + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SingleChoiceSegmentedButtonRow +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.features.poll.api.pollcontent.PollContentView +import io.element.android.features.poll.impl.R +import io.element.android.features.poll.impl.history.model.PollHistoryFilter +import io.element.android.features.poll.impl.history.model.PollHistoryItem +import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.aliasScreenTitle +import io.element.android.libraries.designsystem.theme.components.Button +import io.element.android.libraries.designsystem.theme.components.Scaffold +import io.element.android.libraries.designsystem.theme.components.SegmentedButton +import io.element.android.libraries.designsystem.theme.components.Surface +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.theme.components.TopAppBar +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.ImmutableList + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) +@Composable +fun PollHistoryView( + state: PollHistoryState, + onEditPoll: (EventId) -> Unit, + goBack: () -> Unit, + modifier: Modifier = Modifier, +) { + + fun onLoadMore() { + state.eventSink(PollHistoryEvents.LoadMore) + } + + fun onAnswerSelected(pollStartId: EventId, answerId: String) { + state.eventSink(PollHistoryEvents.PollAnswerSelected(pollStartId, answerId)) + } + + fun onPollEnd(pollStartId: EventId) { + state.eventSink(PollHistoryEvents.PollEndClicked(pollStartId)) + } + + Scaffold( + modifier = modifier, + topBar = { + TopAppBar( + title = { + Text( + text = stringResource(R.string.screen_polls_history_title), + style = ElementTheme.typography.aliasScreenTitle, + ) + }, + navigationIcon = { + BackButton(onClick = goBack) + }, + ) + }, + ) { padding -> + Column( + modifier = Modifier + .padding(padding) + .consumeWindowInsets(padding) + ) { + val pagerState = rememberPagerState(state.activeFilter.ordinal, 0f) { + PollHistoryFilter.entries.size + } + LaunchedEffect(state.activeFilter) { + pagerState.scrollToPage(state.activeFilter.ordinal) + } + PollHistoryFilterButtons( + activeFilter = state.activeFilter, + onFilterSelected = { state.eventSink(PollHistoryEvents.OnFilterSelected(it)) }, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + ) + HorizontalPager( + state = pagerState, + userScrollEnabled = false, + modifier = Modifier.fillMaxSize() + ) { page -> + val filter = PollHistoryFilter.entries[page] + val pollHistoryItems = state.pollHistoryForFilter(filter) + PollHistoryList( + filter = filter, + pollHistoryItems = pollHistoryItems, + hasMoreToLoad = state.hasMoreToLoad, + isLoading = state.isLoading, + onAnswerSelected = ::onAnswerSelected, + onPollEdit = onEditPoll, + onPollEnd = ::onPollEnd, + onLoadMore = ::onLoadMore, + modifier = Modifier.fillMaxSize(), + ) + + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun PollHistoryFilterButtons( + activeFilter: PollHistoryFilter, + onFilterSelected: (PollHistoryFilter) -> Unit, + modifier: Modifier = Modifier, +) { + SingleChoiceSegmentedButtonRow(modifier = modifier) { + PollHistoryFilter.entries.forEach { filter -> + SegmentedButton( + index = filter.ordinal, + count = PollHistoryFilter.entries.size, + selected = activeFilter == filter, + onClick = { onFilterSelected(filter) }, + text = stringResource(filter.stringResource), + ) + } + } +} + +@Composable +private fun PollHistoryList( + filter: PollHistoryFilter, + pollHistoryItems: ImmutableList, + hasMoreToLoad: Boolean, + isLoading: Boolean, + onAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit, + onPollEdit: (pollStartId: EventId) -> Unit, + onPollEnd: (pollStartId: EventId) -> Unit, + onLoadMore: () -> Unit, + modifier: Modifier = Modifier, +) { + val lazyListState = rememberLazyListState() + LazyColumn( + state = lazyListState, + modifier = modifier, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + items(pollHistoryItems) { pollHistoryItem -> + PollHistoryItemRow( + pollHistoryItem = pollHistoryItem, + onAnswerSelected = onAnswerSelected, + onPollEdit = onPollEdit, + onPollEnd = onPollEnd, + modifier = Modifier.padding(vertical = 8.dp, horizontal = 16.dp) + ) + } + if (pollHistoryItems.isEmpty()) { + item { + val emptyStringResource = if (filter == PollHistoryFilter.PAST) { + stringResource(R.string.screen_polls_history_empty_past) + } else { + stringResource(R.string.screen_polls_history_empty_ongoing) + } + Text( + text = emptyStringResource, + style = ElementTheme.typography.fontBodyLgRegular, + color = ElementTheme.colors.textSecondary, + modifier = Modifier.padding(vertical = 24.dp, horizontal = 16.dp) + ) + } + } + if (hasMoreToLoad) { + item { + Button( + text = stringResource(CommonStrings.action_load_more), + showProgress = isLoading, + onClick = onLoadMore, + modifier = Modifier.padding(vertical = 24.dp), + ) + } + } + } +} + +@Composable +private fun PollHistoryItemRow( + pollHistoryItem: PollHistoryItem, + onAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit, + onPollEdit: (pollStartId: EventId) -> Unit, + onPollEnd: (pollStartId: EventId) -> Unit, + modifier: Modifier = Modifier, +) { + Surface( + modifier = modifier, + border = BorderStroke(1.dp, ElementTheme.colors.borderInteractiveSecondary), + shape = RoundedCornerShape(size = 12.dp) + ) { + Column(modifier = Modifier.padding(16.dp)) { + Text( + text = pollHistoryItem.formattedDate, + color = MaterialTheme.colorScheme.secondary, + style = ElementTheme.typography.fontBodySmRegular, + ) + Spacer(modifier = Modifier.height(4.dp)) + PollContentView( + state = pollHistoryItem.state, + onAnswerSelected = onAnswerSelected, + onPollEdit = onPollEdit, + onPollEnd = onPollEnd, + ) + } + } +} + +@PreviewsDayNight +@Composable +internal fun PollHistoryViewPreview( + @PreviewParameter(PollHistoryStateProvider::class) state: PollHistoryState +) = ElementPreview { + PollHistoryView( + state = state, + onEditPoll = {}, + goBack = {}, + ) +} diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/model/PollHistoryFilter.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/model/PollHistoryFilter.kt new file mode 100644 index 0000000000..c8d905e031 --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/model/PollHistoryFilter.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.history.model + +import io.element.android.features.poll.impl.R + +enum class PollHistoryFilter(val stringResource: Int) { + ONGOING(R.string.screen_polls_history_filter_ongoing), + PAST(R.string.screen_polls_history_filter_past), +} diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/model/PollHistoryItem.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/model/PollHistoryItem.kt new file mode 100644 index 0000000000..6b55b249bb --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/model/PollHistoryItem.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.history.model + +import io.element.android.features.poll.api.pollcontent.PollContentState + +data class PollHistoryItem( + val formattedDate: String, + val state: PollContentState, +) diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/model/PollHistoryItems.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/model/PollHistoryItems.kt new file mode 100644 index 0000000000..180e31b458 --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/model/PollHistoryItems.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.history.model + +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf + +data class PollHistoryItems( + val ongoing: ImmutableList = persistentListOf(), + val past: ImmutableList = persistentListOf(), +) { + val size = ongoing.size + past.size +} diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/model/PollHistoryItemsFactory.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/model/PollHistoryItemsFactory.kt new file mode 100644 index 0000000000..a85e4cada2 --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/model/PollHistoryItemsFactory.kt @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.history.model + +import io.element.android.features.poll.api.pollcontent.PollContentStateFactory +import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.dateformatter.api.DaySeparatorFormatter +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +import kotlinx.collections.immutable.toPersistentList +import kotlinx.coroutines.withContext +import javax.inject.Inject + +class PollHistoryItemsFactory @Inject constructor( + private val pollContentStateFactory: PollContentStateFactory, + private val daySeparatorFormatter: DaySeparatorFormatter, + private val dispatchers: CoroutineDispatchers, +) { + + suspend fun create(timelineItems: List): PollHistoryItems = withContext(dispatchers.computation) { + val past = ArrayList() + val ongoing = ArrayList() + for (index in timelineItems.indices.reversed()) { + val timelineItem = timelineItems[index] + val pollHistoryItem = create(timelineItem) ?: continue + if (pollHistoryItem.state.isPollEnded) { + past.add(pollHistoryItem) + } else { + ongoing.add(pollHistoryItem) + } + } + PollHistoryItems( + ongoing = ongoing.toPersistentList(), + past = past.toPersistentList() + ) + } + + private suspend fun create(timelineItem: MatrixTimelineItem): PollHistoryItem? { + return when (timelineItem) { + is MatrixTimelineItem.Event -> { + val pollContent = timelineItem.event.content as? PollContent ?: return null + val pollContentState = pollContentStateFactory.create(timelineItem.event, pollContent) + PollHistoryItem( + formattedDate = daySeparatorFormatter.format(timelineItem.event.timestamp), + state = pollContentState + ) + } + else -> null + } + } +} + diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/model/DefaultPollContentStateFactory.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/model/DefaultPollContentStateFactory.kt new file mode 100644 index 0000000000..cab06f79a1 --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/model/DefaultPollContentStateFactory.kt @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.model + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.poll.api.pollcontent.PollAnswerItem +import io.element.android.features.poll.api.pollcontent.PollContentState +import io.element.android.features.poll.api.pollcontent.PollContentStateFactory +import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.poll.isDisclosed +import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +import kotlinx.collections.immutable.toImmutableList +import javax.inject.Inject + +@ContributesBinding(RoomScope::class) +class DefaultPollContentStateFactory @Inject constructor( + private val matrixClient: MatrixClient, +) : PollContentStateFactory { + + override suspend fun create( + event: EventTimelineItem, + content: PollContent + ): PollContentState { + val totalVoteCount = content.votes.flatMap { it.value }.size + val myVotes = content.votes.filter { matrixClient.sessionId in it.value }.keys + val isPollEnded = content.endTime != null + val winnerIds = if (!isPollEnded) { + emptyList() + } else { + content.answers + .map { answer -> answer.id } + .groupBy { answerId -> content.votes[answerId]?.size ?: 0 } // Group by votes count + .maxByOrNull { (votes, _) -> votes } // Keep max voted answers + ?.takeIf { (votes, _) -> votes > 0 } // Ignore if no option has been voted + ?.value + .orEmpty() + } + val answerItems = content.answers.map { answer -> + val answerVoteCount = content.votes[answer.id]?.size ?: 0 + val isSelected = answer.id in myVotes + val isWinner = answer.id in winnerIds + val percentage = if (totalVoteCount > 0) answerVoteCount.toFloat() / totalVoteCount.toFloat() else 0f + PollAnswerItem( + answer = answer, + isSelected = isSelected, + isEnabled = !isPollEnded, + isWinner = isWinner, + isDisclosed = content.kind.isDisclosed || isPollEnded, + votesCount = answerVoteCount, + percentage = percentage, + ) + } + + return PollContentState( + eventId = event.eventId, + question = content.question, + answerItems = answerItems.toImmutableList(), + pollKind = content.kind, + isPollEditable = event.isEditable, + isPollEnded = isPollEnded, + isMine = event.isOwn, + ) + } +} diff --git a/features/poll/impl/src/main/res/values-cs/translations.xml b/features/poll/impl/src/main/res/values-cs/translations.xml index fa722e9616..ac1ebfb8dd 100644 --- a/features/poll/impl/src/main/res/values-cs/translations.xml +++ b/features/poll/impl/src/main/res/values-cs/translations.xml @@ -4,9 +4,16 @@ "Zobrazit výsledky až po skončení hlasování" "Anonymní hlasování" "Volba %1$d" - "Opravdu chcete zrušit toto hlasování?" - "Zrušit hlasování" + "Vaše změny nebyly uloženy. Opravdu se chcete vrátit?" "Otázka nebo téma" "Čeho se hlasování týká?" "Vytvořit hlasování" + "Opravdu chcete odstranit toto hlasování?" + "Odstranit hlasování" + "Upravit hlasování" + "Nelze najít žádná probíhající hlasování." + "Nelze najít žádná minulá hlasování." + "Probíhající" + "Minulé" + "Hlasování" diff --git a/features/poll/impl/src/main/res/values-de/translations.xml b/features/poll/impl/src/main/res/values-de/translations.xml index bd43eb8337..99520079e6 100644 --- a/features/poll/impl/src/main/res/values-de/translations.xml +++ b/features/poll/impl/src/main/res/values-de/translations.xml @@ -4,9 +4,16 @@ "Ergebnisse erst nach Ende der Umfrage anzeigen" "Anonyme Umfrage" "Option %1$d" - "Bist du sicher, dass du diese Umfrage verwerfen willst?" - "Umfrage verwerfen" + "Deine Änderungen wurden nicht gespeichert. Bist du sicher, dass du zurückgehen willst?" "Frage oder Thema" "Worum geht es bei der Umfrage?" "Umfrage erstellen" + "Bist du dir sicher, dass du diese Umfrage löschen möchtest?" + "Umfrage löschen" + "Umfrage bearbeiten" + "Keine laufenden Umfragen vorhanden." + "Keine vergangenen Umfragen vorhanden." + "Aktuell" + "Vergangene" + "Umfragen" diff --git a/features/poll/impl/src/main/res/values-fr/translations.xml b/features/poll/impl/src/main/res/values-fr/translations.xml index adfcfd274d..081b5e53b1 100644 --- a/features/poll/impl/src/main/res/values-fr/translations.xml +++ b/features/poll/impl/src/main/res/values-fr/translations.xml @@ -4,9 +4,16 @@ "Afficher les résultats uniquement après la fin du sondage" "Masquer les votes" "Option %1$d" - "Êtes-vous sûr de vouloir supprimer ce sondage ?" - "Supprimer le sondage" + "Vos modifications n’ont pas été enregistrées. Êtes-vous certain de vouloir quitter?" "Question ou sujet" "Quel est le sujet du sondage ?" "Créer un sondage" + "Êtes-vous certain de vouloir supprimer ce sondage?" + "Supprimer le sondage" + "Modifier le sondage" + "Impossible de trouver des sondages en cours." + "Impossible de trouver des sondages terminés." + "En cours" + "Terminés" + "Sondages" diff --git a/features/poll/impl/src/main/res/values-hu/translations.xml b/features/poll/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..74b83c8942 --- /dev/null +++ b/features/poll/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,19 @@ + + + "Lehetőség hozzáadása" + "Eredmények megjelenítése csak a szavazás befejezése után" + "Szavazatok elrejtése" + "%1$d. lehetőség" + "A módosítások nem lettek mentve. Biztos, hogy vissza akar lépni?" + "Kérdés vagy téma" + "Miről szól ez a szavazás?" + "Szavazás létrehozása" + "Biztos, hogy törli ezt a szavazást?" + "Szavazás törlése" + "Szavazás szerkesztése" + "Nem találhatók folyamatban lévő szavazások." + "Nem található korábbi szavazás." + "Folyamatban" + "Korábbi" + "Szavazások" + diff --git a/features/poll/impl/src/main/res/values-in/translations.xml b/features/poll/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..b8982d9c13 --- /dev/null +++ b/features/poll/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,14 @@ + + + "Tambahkan opsi" + "Tampilkan hasil hanya setelah pemungutan suara berakhir" + "Pemungutan suara anonim" + "Opsi %1$d" + "Perubahan Anda belum disimpan. Apakah Anda yakin ingin kembali?" + "Pertanyaan atau topik" + "Tentang apa pemungutan suara ini?" + "Buat pemungutan suara" + "Apakah Anda yakin ingin menghapus pemungutan suara ini?" + "Hapus pemungutan suara" + "Sunting pemungutan suara" + diff --git a/features/poll/impl/src/main/res/values-ro/translations.xml b/features/poll/impl/src/main/res/values-ro/translations.xml index b552a68024..a7ecdcc4dc 100644 --- a/features/poll/impl/src/main/res/values-ro/translations.xml +++ b/features/poll/impl/src/main/res/values-ro/translations.xml @@ -4,8 +4,6 @@ "Afișați rezultatele numai după încheierea sondajului" "Sondaj anonim" "Opțiune %1$d" - "Sunteți sigur că doriți să renunțați la acest sondaj?" - "Renunțați la sondaj" "Întrebare sau subiect" "Despre ce este sondajul?" "Creați un sondaj" diff --git a/features/poll/impl/src/main/res/values-ru/translations.xml b/features/poll/impl/src/main/res/values-ru/translations.xml index c471ff9dd2..65318d3830 100644 --- a/features/poll/impl/src/main/res/values-ru/translations.xml +++ b/features/poll/impl/src/main/res/values-ru/translations.xml @@ -4,9 +4,16 @@ "Показывать результаты только после окончания опроса" "Анонимный опрос" "Настройка %1$d" - "Вы действительно хотите отменить этот опрос?" - "Отменить опрос" + "Изменения не сохранены. Вы действительно хотите вернуться?" "Вопрос или тема" "Тема опроса?" "Создать опрос" + "Вы уверены, что хотите удалить этот опрос?" + "Удалить опрос" + "Редактировать опрос" + "Не найдено текущих опросов." + "Не найдено предыдущих опросов." + "Текущие" + "Прошедшие" + "Опросы" diff --git a/features/poll/impl/src/main/res/values-sk/translations.xml b/features/poll/impl/src/main/res/values-sk/translations.xml index 5e2d9ee7bc..cbc6b8582c 100644 --- a/features/poll/impl/src/main/res/values-sk/translations.xml +++ b/features/poll/impl/src/main/res/values-sk/translations.xml @@ -4,9 +4,16 @@ "Zobraziť výsledky až po skončení ankety" "Anonymná anketa" "Možnosť %1$d" - "Ste si istí, že chcete túto anketu zahodiť?" - "Odstrániť anketu" + "Vaše zmeny neboli uložené. Určite sa chcete vrátiť späť?" "Otázka alebo téma" "O čom je anketa?" "Vytvoriť anketu" + "Ste si istý, že chcete odstrániť túto anketu?" + "Odstrániť anketu" + "Upraviť anketu" + "Nepodarilo sa nájsť žiadne prebiehajúce ankety." + "Nie je možné nájsť žiadne predchádzajúce ankety." + "Prebiehajúce" + "Minulé" + "Ankety" diff --git a/features/poll/impl/src/main/res/values-zh-rTW/translations.xml b/features/poll/impl/src/main/res/values-zh-rTW/translations.xml index 298224c8ad..03bd96aa28 100644 --- a/features/poll/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/poll/impl/src/main/res/values-zh-rTW/translations.xml @@ -4,8 +4,6 @@ "只在投票結束後顯示結果" "隱藏票數" "選項 %1$d" - "您確定要捨棄這項投票嗎?" - "捨棄投票" "問題或主題" "投什麼?" "建立投票" diff --git a/features/poll/impl/src/main/res/values/localazy.xml b/features/poll/impl/src/main/res/values/localazy.xml index 3c5006d0af..7a7a15ea3c 100644 --- a/features/poll/impl/src/main/res/values/localazy.xml +++ b/features/poll/impl/src/main/res/values/localazy.xml @@ -4,9 +4,16 @@ "Show results only after poll ends" "Hide votes" "Option %1$d" - "Are you sure you want to discard this poll?" - "Discard Poll" + "Your changes have not been saved. Are you sure you want to go back?" "Question or topic" "What is the poll about?" "Create Poll" + "Are you sure you want to delete this poll?" + "Delete Poll" + "Edit poll" + "Can\'t find any ongoing polls." + "Can\'t find any past polls." + "Ongoing" + "Past" + "Polls" diff --git a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/PollFixtures.kt b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/PollFixtures.kt new file mode 100644 index 0000000000..b54b18b8c0 --- /dev/null +++ b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/PollFixtures.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl + +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.poll.PollAnswer +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline +import io.element.android.libraries.matrix.test.timeline.aPollContent +import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem +import kotlinx.collections.immutable.persistentListOf + +fun aPollTimeline( + polls: Map = emptyMap(), +): FakeMatrixTimeline { + return FakeMatrixTimeline( + initialTimelineItems = polls.map { entry -> + MatrixTimelineItem.Event( + entry.key.value, + anEventTimelineItem( + eventId = entry.key, + content = entry.value, + ) + ) + } + ) +} + +fun anOngoingPollContent() = aPollContent( + question = "Do you like polls?", + answers = persistentListOf( + PollAnswer("1", "Yes"), + PollAnswer("2", "No"), + PollAnswer("2", "Maybe"), + ), +) + +fun anEndedPollContent() = anOngoingPollContent().copy( + endTime = 1702400215U +) diff --git a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenterTest.kt b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenterTest.kt index 4ea3ff1eca..5ee32efeb2 100644 --- a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenterTest.kt +++ b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenterTest.kt @@ -18,14 +18,22 @@ package io.element.android.features.poll.impl.create import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow +import app.cash.turbine.TurbineTestContext import app.cash.turbine.test -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.Composer import im.vector.app.features.analytics.plan.PollCreation import io.element.android.features.messages.test.FakeMessageComposerContext +import io.element.android.features.poll.api.create.CreatePollMode +import io.element.android.features.poll.impl.aPollTimeline +import io.element.android.features.poll.impl.anOngoingPollContent +import io.element.android.features.poll.impl.data.PollRepository import io.element.android.libraries.matrix.api.poll.PollKind -import io.element.android.libraries.matrix.test.room.CreatePollInvocation +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.SavePollInvocation import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.WarmUpRule import kotlinx.coroutines.delay @@ -38,58 +46,79 @@ class CreatePollPresenterTest { @get:Rule val warmUpRule = WarmUpRule() + private val pollEventId = AN_EVENT_ID private var navUpInvocationsCount = 0 - private val fakeMatrixRoom = FakeMatrixRoom() + private val existingPoll = anOngoingPollContent() + private val fakeMatrixRoom = FakeMatrixRoom( + matrixTimeline = aPollTimeline( + mapOf(pollEventId to existingPoll) + ) + ) private val fakeAnalyticsService = FakeAnalyticsService() private val fakeMessageComposerContext = FakeMessageComposerContext() - private val presenter = CreatePollPresenter( - room = fakeMatrixRoom, - analyticsService = fakeAnalyticsService, - messageComposerContext = fakeMessageComposerContext, - navigateUp = { navUpInvocationsCount++ }, - ) - @Test fun `default state has proper default values`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - awaitItem().let { - Truth.assertThat(it.canCreate).isEqualTo(false) - Truth.assertThat(it.canAddAnswer).isEqualTo(true) - Truth.assertThat(it.question).isEqualTo("") - Truth.assertThat(it.answers).isEqualTo(listOf(Answer("", false), Answer("", false))) - Truth.assertThat(it.pollKind).isEqualTo(PollKind.Disclosed) - Truth.assertThat(it.showConfirmation).isEqualTo(false) - } + awaitDefaultItem() + } + } + + @Test + fun `in edit mode, poll values are loaded`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(AN_EVENT_ID)) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitDefaultItem() + awaitPollLoaded() + } + } + + @Test + fun `in edit mode, if poll doesn't exist, error is tracked and screen is closed`() = runTest { + val room = FakeMatrixRoom( + matrixTimeline = aPollTimeline() + ) + val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(AN_EVENT_ID), room = room) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitDefaultItem() + assertThat(fakeAnalyticsService.trackedErrors.filterIsInstance()).isNotEmpty() + assertThat(navUpInvocationsCount).isEqualTo(1) } } @Test fun `non blank question and 2 answers are required to create a poll`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initial = awaitItem() - Truth.assertThat(initial.canCreate).isEqualTo(false) + assertThat(initial.canSave).isFalse() initial.eventSink(CreatePollEvents.SetQuestion("A question?")) val questionSet = awaitItem() - Truth.assertThat(questionSet.canCreate).isEqualTo(false) + assertThat(questionSet.canSave).isFalse() questionSet.eventSink(CreatePollEvents.SetAnswer(0, "Answer 1")) val answer1Set = awaitItem() - Truth.assertThat(answer1Set.canCreate).isEqualTo(false) + assertThat(answer1Set.canSave).isFalse() answer1Set.eventSink(CreatePollEvents.SetAnswer(1, "Answer 2")) val answer2Set = awaitItem() - Truth.assertThat(answer2Set.canCreate).isEqualTo(true) + assertThat(answer2Set.canSave).isTrue() } } @Test - fun `create polls sends a poll start event`() = runTest { + fun `create poll sends a poll start event`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -98,19 +127,19 @@ class CreatePollPresenterTest { initial.eventSink(CreatePollEvents.SetAnswer(0, "Answer 1")) initial.eventSink(CreatePollEvents.SetAnswer(1, "Answer 2")) skipItems(3) - initial.eventSink(CreatePollEvents.Create) + initial.eventSink(CreatePollEvents.Save) delay(1) // Wait for the coroutine to finish - Truth.assertThat(fakeMatrixRoom.createPollInvocations.size).isEqualTo(1) - Truth.assertThat(fakeMatrixRoom.createPollInvocations.last()).isEqualTo( - CreatePollInvocation( + assertThat(fakeMatrixRoom.createPollInvocations.size).isEqualTo(1) + assertThat(fakeMatrixRoom.createPollInvocations.last()).isEqualTo( + SavePollInvocation( question = "A question?", answers = listOf("Answer 1", "Answer 2"), maxSelections = 1, pollKind = PollKind.Disclosed ) ) - Truth.assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(2) - Truth.assertThat(fakeAnalyticsService.capturedEvents[0]).isEqualTo( + assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(2) + assertThat(fakeAnalyticsService.capturedEvents[0]).isEqualTo( Composer( inThread = false, isEditing = false, @@ -118,7 +147,7 @@ class CreatePollPresenterTest { messageType = Composer.MessageType.Poll, ) ) - Truth.assertThat(fakeAnalyticsService.capturedEvents[1]).isEqualTo( + assertThat(fakeAnalyticsService.capturedEvents[1]).isEqualTo( PollCreation( action = PollCreation.Action.Create, isUndisclosed = false, @@ -128,139 +157,373 @@ class CreatePollPresenterTest { } } + @Test + fun `when poll creation fails, error is tracked`() = runTest { + val error = Exception("cause") + fakeMatrixRoom.givenCreatePollResult(Result.failure(error)) + val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitDefaultItem().eventSink(CreatePollEvents.SetQuestion("A question?")) + awaitItem().eventSink(CreatePollEvents.SetAnswer(0, "Answer 1")) + awaitItem().eventSink(CreatePollEvents.SetAnswer(1, "Answer 2")) + awaitItem().eventSink(CreatePollEvents.Save) + delay(1) // Wait for the coroutine to finish + assertThat(fakeMatrixRoom.createPollInvocations).hasSize(1) + assertThat(fakeAnalyticsService.capturedEvents).isEmpty() + assertThat(fakeAnalyticsService.trackedErrors).hasSize(1) + assertThat(fakeAnalyticsService.trackedErrors).containsExactly( + CreatePollException.SavePollFailed("Failed to create poll", error) + ) + } + } + + @Test + fun `edit poll sends a poll edit event`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId)) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitDefaultItem() + awaitPollLoaded().apply { + eventSink(CreatePollEvents.SetQuestion("Changed question")) + } + awaitItem().apply { + eventSink(CreatePollEvents.SetAnswer(0, "Changed answer 1")) + } + awaitItem().apply { + eventSink(CreatePollEvents.SetAnswer(1, "Changed answer 2")) + } + awaitPollLoaded( + newQuestion = "Changed question", + newAnswer1 = "Changed answer 1", + newAnswer2 = "Changed answer 2", + ).apply { + eventSink(CreatePollEvents.Save) + } + delay(1) // Wait for the coroutine to finish + assertThat(fakeMatrixRoom.editPollInvocations.size).isEqualTo(1) + assertThat(fakeMatrixRoom.editPollInvocations.last()).isEqualTo( + SavePollInvocation( + question = "Changed question", + answers = listOf("Changed answer 1", "Changed answer 2", "Maybe"), + maxSelections = 1, + pollKind = PollKind.Disclosed + ) + ) + assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(2) + assertThat(fakeAnalyticsService.capturedEvents[0]).isEqualTo( + Composer( + inThread = false, + isEditing = true, + isReply = false, + messageType = Composer.MessageType.Poll, + ) + ) + assertThat(fakeAnalyticsService.capturedEvents[1]).isEqualTo( + PollCreation( + action = PollCreation.Action.Edit, + isUndisclosed = false, + numberOfAnswers = 3, + ) + ) + } + } + + @Test + fun `when edit poll fails, error is tracked`() = runTest { + val error = Exception("cause") + fakeMatrixRoom.givenEditPollResult(Result.failure(error)) + val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId)) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitDefaultItem() + awaitPollLoaded().eventSink(CreatePollEvents.SetAnswer(0, "A")) + awaitPollLoaded(newAnswer1 = "A").eventSink(CreatePollEvents.Save) + delay(1) // Wait for the coroutine to finish + assertThat(fakeMatrixRoom.editPollInvocations).hasSize(1) + assertThat(fakeAnalyticsService.capturedEvents).isEmpty() + assertThat(fakeAnalyticsService.trackedErrors).hasSize(1) + assertThat(fakeAnalyticsService.trackedErrors).containsExactly( + CreatePollException.SavePollFailed("Failed to edit poll", error) + ) + } + } + @Test fun `add answer button adds an empty answer and removing it removes it`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initial = awaitItem() - Truth.assertThat(initial.answers.size).isEqualTo(2) + assertThat(initial.answers.size).isEqualTo(2) initial.eventSink(CreatePollEvents.AddAnswer) val answerAdded = awaitItem() - Truth.assertThat(answerAdded.answers.size).isEqualTo(3) - Truth.assertThat(answerAdded.answers[2].text).isEqualTo("") + assertThat(answerAdded.answers.size).isEqualTo(3) + assertThat(answerAdded.answers[2].text).isEmpty() initial.eventSink(CreatePollEvents.RemoveAnswer(2)) val answerRemoved = awaitItem() - Truth.assertThat(answerRemoved.answers.size).isEqualTo(2) + assertThat(answerRemoved.answers.size).isEqualTo(2) } } @Test fun `set question sets the question`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initial = awaitItem() initial.eventSink(CreatePollEvents.SetQuestion("A question?")) val questionSet = awaitItem() - Truth.assertThat(questionSet.question).isEqualTo("A question?") + assertThat(questionSet.question).isEqualTo("A question?") } } @Test fun `set poll answer sets the given poll answer`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initial = awaitItem() initial.eventSink(CreatePollEvents.SetAnswer(0, "This is answer 1")) val answerSet = awaitItem() - Truth.assertThat(answerSet.answers.first().text).isEqualTo("This is answer 1") + assertThat(answerSet.answers.first().text).isEqualTo("This is answer 1") } } @Test fun `set poll kind sets the poll kind`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initial = awaitItem() initial.eventSink(CreatePollEvents.SetPollKind(PollKind.Undisclosed)) val kindSet = awaitItem() - Truth.assertThat(kindSet.pollKind).isEqualTo(PollKind.Undisclosed) + assertThat(kindSet.pollKind).isEqualTo(PollKind.Undisclosed) } } @Test fun `can add options when between 2 and 20 and then no more`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initial = awaitItem() - Truth.assertThat(initial.canAddAnswer).isEqualTo(true) + assertThat(initial.canAddAnswer).isTrue() repeat(17) { initial.eventSink(CreatePollEvents.AddAnswer) - Truth.assertThat(awaitItem().canAddAnswer).isEqualTo(true) + assertThat(awaitItem().canAddAnswer).isTrue() } initial.eventSink(CreatePollEvents.AddAnswer) - Truth.assertThat(awaitItem().canAddAnswer).isEqualTo(false) + assertThat(awaitItem().canAddAnswer).isFalse() } } @Test fun `can delete option if there are more than 2`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initial = awaitItem() - Truth.assertThat(initial.answers.all { it.canDelete }).isEqualTo(false) + assertThat(initial.answers.all { it.canDelete }).isFalse() initial.eventSink(CreatePollEvents.AddAnswer) - Truth.assertThat(awaitItem().answers.all { it.canDelete }).isEqualTo(true) + assertThat(awaitItem().answers.all { it.canDelete }).isTrue() } } @Test fun `option with more than 240 char is truncated`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initial = awaitItem() initial.eventSink(CreatePollEvents.SetAnswer(0, "A".repeat(241))) - Truth.assertThat(awaitItem().answers.first().text.length).isEqualTo(240) + assertThat(awaitItem().answers.first().text.length).isEqualTo(240) } } @Test fun `navBack event calls navBack lambda`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initial = awaitItem() - Truth.assertThat(navUpInvocationsCount).isEqualTo(0) + assertThat(navUpInvocationsCount).isEqualTo(0) initial.eventSink(CreatePollEvents.NavBack) - Truth.assertThat(navUpInvocationsCount).isEqualTo(1) + assertThat(navUpInvocationsCount).isEqualTo(1) } } @Test - fun `confirm nav back with blank fields calls nav back lambda`() = runTest { + fun `confirm nav back from new poll with blank fields calls nav back lambda`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initial = awaitItem() - Truth.assertThat(navUpInvocationsCount).isEqualTo(0) - Truth.assertThat(initial.showConfirmation).isEqualTo(false) + assertThat(navUpInvocationsCount).isEqualTo(0) + assertThat(initial.showBackConfirmation).isFalse() initial.eventSink(CreatePollEvents.ConfirmNavBack) - Truth.assertThat(navUpInvocationsCount).isEqualTo(1) + assertThat(navUpInvocationsCount).isEqualTo(1) } } @Test - fun `confirm nav back with non blank fields shows confirmation dialog and sending hide hids it`() = runTest { + fun `confirm nav back from new poll with non blank fields shows confirmation dialog and cancelling hides it`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initial = awaitItem() initial.eventSink(CreatePollEvents.SetQuestion("Non blank")) - Truth.assertThat(navUpInvocationsCount).isEqualTo(0) - Truth.assertThat(awaitItem().showConfirmation).isEqualTo(false) + assertThat(awaitItem().showBackConfirmation).isFalse() initial.eventSink(CreatePollEvents.ConfirmNavBack) - Truth.assertThat(navUpInvocationsCount).isEqualTo(0) - Truth.assertThat(awaitItem().showConfirmation).isEqualTo(true) + assertThat(awaitItem().showBackConfirmation).isTrue() initial.eventSink(CreatePollEvents.HideConfirmation) - Truth.assertThat(awaitItem().showConfirmation).isEqualTo(false) + assertThat(awaitItem().showBackConfirmation).isFalse() + assertThat(navUpInvocationsCount).isEqualTo(0) } } + + @Test + fun `confirm nav back from existing poll with unchanged fields calls nav back lambda`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId)) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitDefaultItem() + val loaded = awaitPollLoaded() + assertThat(navUpInvocationsCount).isEqualTo(0) + assertThat(loaded.showBackConfirmation).isFalse() + loaded.eventSink(CreatePollEvents.ConfirmNavBack) + assertThat(navUpInvocationsCount).isEqualTo(1) + } + } + + @Test + fun `confirm nav back from existing poll with changed fields shows confirmation dialog and cancelling hides it`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId)) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitDefaultItem() + val loaded = awaitPollLoaded() + loaded.eventSink(CreatePollEvents.SetQuestion("CHANGED")) + assertThat(awaitItem().showBackConfirmation).isFalse() + loaded.eventSink(CreatePollEvents.ConfirmNavBack) + assertThat(awaitItem().showBackConfirmation).isTrue() + loaded.eventSink(CreatePollEvents.HideConfirmation) + assertThat(awaitItem().showBackConfirmation).isFalse() + assertThat(navUpInvocationsCount).isEqualTo(0) + } + } + + @Test + fun `delete confirms`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId)) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitDefaultItem() + awaitPollLoaded().eventSink(CreatePollEvents.Delete(confirmed = false)) + awaitDeleteConfirmation() + assertThat(fakeMatrixRoom.redactEventEventIdParam).isNull() + } + } + + @Test + fun `delete can be cancelled`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId)) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitDefaultItem() + awaitPollLoaded().eventSink(CreatePollEvents.Delete(confirmed = false)) + assertThat(fakeMatrixRoom.redactEventEventIdParam).isNull() + awaitDeleteConfirmation().eventSink(CreatePollEvents.HideConfirmation) + awaitPollLoaded().apply { + assertThat(showDeleteConfirmation).isFalse() + } + assertThat(fakeMatrixRoom.redactEventEventIdParam).isNull() + } + } + + @Test + fun `delete can be confirmed`() = runTest { + val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId)) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitDefaultItem() + awaitPollLoaded().eventSink(CreatePollEvents.Delete(confirmed = false)) + assertThat(fakeMatrixRoom.redactEventEventIdParam).isNull() + awaitDeleteConfirmation().eventSink(CreatePollEvents.Delete(confirmed = true)) + awaitPollLoaded().apply { + assertThat(showDeleteConfirmation).isFalse() + } + assertThat(fakeMatrixRoom.redactEventEventIdParam).isEqualTo(pollEventId) + } + } + + private suspend fun TurbineTestContext.awaitDefaultItem() = + awaitItem().apply { + assertThat(canSave).isFalse() + assertThat(canAddAnswer).isTrue() + assertThat(question).isEmpty() + assertThat(answers).isEqualTo(listOf(Answer("", false), Answer("", false))) + assertThat(pollKind).isEqualTo(PollKind.Disclosed) + assertThat(showBackConfirmation).isFalse() + assertThat(showDeleteConfirmation).isFalse() + } + + private suspend fun TurbineTestContext.awaitDeleteConfirmation() = + awaitItem().apply { + assertThat(showDeleteConfirmation).isTrue() + } + + private suspend fun TurbineTestContext.awaitPollLoaded( + newQuestion: String? = null, + newAnswer1: String? = null, + newAnswer2: String? = null, + ) = + awaitItem().apply { + assertThat(canSave).isTrue() + assertThat(canAddAnswer).isTrue() + assertThat(question).isEqualTo(newQuestion ?: existingPoll.question) + assertThat(answers).isEqualTo(existingPoll.expectedAnswersState().toMutableList().apply { + newAnswer1?.let { this[0] = Answer(it, true) } + newAnswer2?.let { this[1] = Answer(it, true) } + }) + assertThat(pollKind).isEqualTo(existingPoll.kind) + } + + private fun createCreatePollPresenter( + mode: CreatePollMode = CreatePollMode.NewPoll, + room: MatrixRoom = fakeMatrixRoom, + ): CreatePollPresenter = CreatePollPresenter( + repository = PollRepository(room), + analyticsService = fakeAnalyticsService, + messageComposerContext = fakeMessageComposerContext, + navigateUp = { navUpInvocationsCount++ }, + mode = mode, + ) +} + +private fun PollContent.expectedAnswersState() = answers.map { answer -> + Answer( + text = answer.text, + canDelete = answers.size > 2, + ) } diff --git a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/PollFormStateSaverTest.kt b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/PollFormStateSaverTest.kt new file mode 100644 index 0000000000..62b3918372 --- /dev/null +++ b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/PollFormStateSaverTest.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.create + +import androidx.compose.runtime.saveable.SaverScope +import com.google.common.truth.Truth.assertThat +import kotlinx.collections.immutable.toPersistentList +import org.junit.Test + +class PollFormStateSaverTest { + companion object { + val CanSaveScope = SaverScope { true } + } + @Test + fun `test save and restore`() { + val state = PollFormState( + question = "question", + answers = listOf("answer1", "answer2").toPersistentList(), + isDisclosed = true, + ) + + val saved = with(CanSaveScope) { + with(pollFormStateSaver) { + save(state) + } + } + + val restored = saved?.let { + pollFormStateSaver.restore(it) + } + + assertThat(restored).isEqualTo(state) + } +} diff --git a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/PollFormStateTest.kt b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/PollFormStateTest.kt new file mode 100644 index 0000000000..cf97967f23 --- /dev/null +++ b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/PollFormStateTest.kt @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.create + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.poll.impl.PollConstants +import io.element.android.libraries.matrix.api.poll.PollKind +import kotlinx.collections.immutable.toPersistentList +import org.junit.Test + +class PollFormStateTest { + + @Test + fun `with new answer`() { + val state = PollFormState.Empty + val newState = state.withNewAnswer() + assertThat(newState.answers).isEqualTo(listOf("", "", "")) + } + + @Test + fun `with new answer, given cannot add, doesn't add`() { + val state = PollFormState.Empty.withBlankAnswers(PollConstants.MAX_ANSWERS) + val newState = state.withNewAnswer() + assertThat(newState).isEqualTo(state) + } + + @Test + fun `with answer deleted, given cannot delete, doesn't delete`() { + val state = PollFormState.Empty + val newState = state.withAnswerRemoved(0) + assertThat(newState).isEqualTo(state) + } + + @Test + fun `with answer deleted, given can delete`() { + val state = PollFormState.Empty.withNewAnswer() + val newState = state.withAnswerRemoved(0) + assertThat(newState).isEqualTo(PollFormState.Empty) + } + + @Test + fun `with answer changed`() { + val state = PollFormState.Empty + val newState = state.withAnswerChanged(1, "New answer") + assertThat(newState).isEqualTo(PollFormState.Empty.copy( + answers = listOf("", "New answer").toPersistentList() + )) + } + + @Test + fun `with answer changed, given it is too long, truncates`() { + val tooLongAnswer = "a".repeat(PollConstants.MAX_ANSWER_LENGTH * 2) + val truncatedAnswer = "a".repeat(PollConstants.MAX_ANSWER_LENGTH) + val state = PollFormState.Empty + val newState = state.withAnswerChanged(1, tooLongAnswer) + assertThat(newState).isEqualTo(PollFormState.Empty.copy( + answers = listOf("", truncatedAnswer).toPersistentList() + )) + } + + @Test + fun `can add answer is true when it does not have max answers`() { + val state = PollFormState.Empty.withBlankAnswers(PollConstants.MAX_ANSWERS - 1) + assertThat(state.canAddAnswer).isTrue() + } + + @Test + fun `can add answer is false when it has max answers`() { + val state = PollFormState.Empty.withBlankAnswers(PollConstants.MAX_ANSWERS) + assertThat(state.canAddAnswer).isFalse() + } + + @Test + fun `can delete answer is false when it has min answers`() { + val state = PollFormState.Empty.withBlankAnswers(PollConstants.MIN_ANSWERS) + assertThat(state.canDeleteAnswer).isFalse() + } + + @Test + fun `can delete answer is true when it has more than min answers`() { + val numAnswers = PollConstants.MIN_ANSWERS + 1 + val state = PollFormState.Empty.withBlankAnswers(numAnswers) + assertThat(state.canDeleteAnswer).isTrue() + } + + @Test + fun `is valid is true when it is valid`() { + val state = aValidPollFormState() + assertThat(state.isValid).isTrue() + } + + @Test + fun `is valid is false when question is blank`() { + val state = aValidPollFormState().copy(question = "") + assertThat(state.isValid).isFalse() + } + + @Test + fun `is valid is false when not enough answers`() { + val state = aValidPollFormState().copy(answers = listOf("").toPersistentList()) + assertThat(state.isValid).isFalse() + } + + @Test + fun `is valid is false when one answer is blank`() { + val state = aValidPollFormState().withNewAnswer() + assertThat(state.isValid).isFalse() + } + + @Test + fun `poll kind when is disclosed`() { + val state = PollFormState.Empty.copy(isDisclosed = true) + assertThat(state.pollKind).isEqualTo(PollKind.Disclosed) + } + + @Test + fun `poll kind when is not disclosed`() { + val state = PollFormState.Empty.copy(isDisclosed = false) + assertThat(state.pollKind).isEqualTo(PollKind.Undisclosed) + } +} + + +private fun aValidPollFormState(): PollFormState { + return PollFormState.Empty.copy( + question = "question", + answers = listOf("answer1", "answer2").toPersistentList(), + isDisclosed = true, + ) +} + +private fun PollFormState.withBlankAnswers(numAnswers: Int): PollFormState = + copy(answers = List(numAnswers) { "" }.toPersistentList()) diff --git a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/PollHistoryPresenterTest.kt b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/PollHistoryPresenterTest.kt new file mode 100644 index 0000000000..39494e1f34 --- /dev/null +++ b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/PollHistoryPresenterTest.kt @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.history + +import app.cash.molecule.RecompositionMode +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.features.poll.api.actions.EndPollAction +import io.element.android.features.poll.api.actions.SendPollResponseAction +import io.element.android.features.poll.impl.aPollTimeline +import io.element.android.features.poll.impl.anEndedPollContent +import io.element.android.features.poll.impl.anOngoingPollContent +import io.element.android.features.poll.impl.history.model.PollHistoryFilter +import io.element.android.features.poll.impl.history.model.PollHistoryItemsFactory +import io.element.android.features.poll.impl.model.DefaultPollContentStateFactory +import io.element.android.features.poll.test.actions.FakeEndPollAction +import io.element.android.features.poll.test.actions.FakeSendPollResponseAction +import io.element.android.libraries.dateformatter.test.FakeDaySeparatorFormatter +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.AN_EVENT_ID_2 +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.consumeItemsUntilPredicate +import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test + +class PollHistoryPresenterTest { + + @get:Rule + val warmUpRule = WarmUpRule() + + private val timeline = aPollTimeline( + polls = mapOf( + AN_EVENT_ID to anOngoingPollContent(), + AN_EVENT_ID_2 to anEndedPollContent() + ) + ) + private val room = FakeMatrixRoom( + matrixTimeline = timeline + ) + + @Test + fun `present - initial states`() = runTest { + val presenter = createPollHistoryPresenter(room = room) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem().also { state -> + assertThat(state.activeFilter).isEqualTo(PollHistoryFilter.ONGOING) + assertThat(state.pollHistoryItems.size).isEqualTo(0) + assertThat(state.isLoading).isTrue() + assertThat(state.hasMoreToLoad).isTrue() + } + consumeItemsUntilPredicate { + it.pollHistoryItems.size == 2 + }.last().also { state -> + assertThat(state.pollHistoryItems.size).isEqualTo(2) + assertThat(state.pollHistoryItems.ongoing).hasSize(1) + assertThat(state.pollHistoryItems.past).hasSize(1) + } + } + } + + @Test + fun `present - change filter scenario`() = runTest { + val presenter = createPollHistoryPresenter(room = room) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem().also { state -> + assertThat(state.activeFilter).isEqualTo(PollHistoryFilter.ONGOING) + state.eventSink(PollHistoryEvents.OnFilterSelected(PollHistoryFilter.PAST)) + } + consumeItemsUntilPredicate { + it.activeFilter == PollHistoryFilter.PAST + }.last().also { state -> + state.eventSink(PollHistoryEvents.OnFilterSelected(PollHistoryFilter.ONGOING)) + } + consumeItemsUntilPredicate { + it.activeFilter == PollHistoryFilter.ONGOING + } + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `present - poll actions scenario`() = runTest { + val sendPollResponseAction = FakeSendPollResponseAction() + val endPollAction = FakeEndPollAction() + val presenter = createPollHistoryPresenter( + sendPollResponseAction = sendPollResponseAction, + endPollAction = endPollAction + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val state = awaitItem() + state.eventSink(PollHistoryEvents.PollEndClicked(AN_EVENT_ID)) + runCurrent() + endPollAction.verifyExecutionCount(1) + state.eventSink(PollHistoryEvents.PollAnswerSelected(AN_EVENT_ID, "answer")) + runCurrent() + sendPollResponseAction.verifyExecutionCount(1) + cancelAndConsumeRemainingEvents() + } + } + + @Test + fun `present - load more scenario`() = runTest { + val presenter = createPollHistoryPresenter(room = room) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + consumeItemsUntilPredicate { + it.pollHistoryItems.size == 2 + } + timeline.updatePaginationState { + copy(isBackPaginating = false) + } + val loadedState = awaitItem() + assertThat(loadedState.isLoading).isFalse() + loadedState.eventSink(PollHistoryEvents.LoadMore) + consumeItemsUntilPredicate { + it.isLoading + } + consumeItemsUntilPredicate { + !it.isLoading + } + } + } + + private fun TestScope.createPollHistoryPresenter( + room: MatrixRoom = FakeMatrixRoom(), + appCoroutineScope: CoroutineScope = this, + endPollAction: EndPollAction = FakeEndPollAction(), + sendPollResponseAction: SendPollResponseAction = FakeSendPollResponseAction(), + pollHistoryItemFactory: PollHistoryItemsFactory = PollHistoryItemsFactory( + pollContentStateFactory = DefaultPollContentStateFactory(FakeMatrixClient()), + daySeparatorFormatter = FakeDaySeparatorFormatter(), + dispatchers = testCoroutineDispatchers(), + ), + ): PollHistoryPresenter { + return PollHistoryPresenter( + room = room, + appCoroutineScope = appCoroutineScope, + sendPollResponseAction = sendPollResponseAction, + endPollAction = endPollAction, + pollHistoryItemFactory = pollHistoryItemFactory, + ) + } +} diff --git a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/pollcontent/PollContentStateFactoryTest.kt b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/pollcontent/PollContentStateFactoryTest.kt new file mode 100644 index 0000000000..fb2f42abf3 --- /dev/null +++ b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/pollcontent/PollContentStateFactoryTest.kt @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.impl.pollcontent + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.poll.api.pollcontent.PollAnswerItem +import io.element.android.features.poll.api.pollcontent.PollContentState +import io.element.android.features.poll.impl.model.DefaultPollContentStateFactory +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.poll.PollAnswer +import io.element.android.libraries.matrix.api.poll.PollKind +import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.A_USER_ID_10 +import io.element.android.libraries.matrix.test.A_USER_ID_2 +import io.element.android.libraries.matrix.test.A_USER_ID_3 +import io.element.android.libraries.matrix.test.A_USER_ID_4 +import io.element.android.libraries.matrix.test.A_USER_ID_5 +import io.element.android.libraries.matrix.test.A_USER_ID_6 +import io.element.android.libraries.matrix.test.A_USER_ID_7 +import io.element.android.libraries.matrix.test.A_USER_ID_8 +import io.element.android.libraries.matrix.test.A_USER_ID_9 +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.ImmutableMap +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentMapOf +import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.toImmutableMap +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class PollContentStateFactoryTest { + + private val factory = DefaultPollContentStateFactory(FakeMatrixClient()) + private val eventTimelineItem = anEventTimelineItem() + + @Test + fun `Disclosed poll - not ended, no votes`() = runTest { + val state = factory.create(eventTimelineItem, aPollContent()) + val expectedState = aPollContentState() + assertThat(state).isEqualTo(expectedState) + } + + @Test + fun `Disclosed poll - not ended, some votes, including one from current user`() = runTest { + val votes = MY_USER_WINNING_VOTES.mapKeys { it.key.id }.toImmutableMap() + val state = factory.create( + eventTimelineItem, aPollContent(votes = votes) + ) + val expectedState = aPollContentState( + answerItems = listOf( + aPollAnswerItem(answer = A_POLL_ANSWER_1, votesCount = 3, percentage = 0.3f), + aPollAnswerItem(answer = A_POLL_ANSWER_2, isSelected = true, votesCount = 6, percentage = 0.6f), + aPollAnswerItem(answer = A_POLL_ANSWER_3), + aPollAnswerItem(answer = A_POLL_ANSWER_4, votesCount = 1, percentage = 0.1f), + ) + ) + assertThat(state).isEqualTo(expectedState) + } + + @Test + fun `Disclosed poll - ended, no votes, no winner`() = runTest { + val state = factory.create(eventTimelineItem, aPollContent(endTime = 1UL)) + val expectedState = aPollContentState().let { + it.copy( + answerItems = it.answerItems.map { answerItem -> answerItem.copy(isEnabled = false) }.toImmutableList(), + isPollEnded = true, + ) + } + assertThat(state).isEqualTo(expectedState) + } + + @Test + fun `Disclosed poll - ended, some votes, including one from current user (winner)`() = runTest { + val votes = MY_USER_WINNING_VOTES.mapKeys { it.key.id }.toImmutableMap() + val state = factory.create( + eventTimelineItem, aPollContent(votes = votes, endTime = 1UL) + ) + val expectedState = aPollContentState( + answerItems = listOf( + aPollAnswerItem(answer = A_POLL_ANSWER_1, isEnabled = false, votesCount = 3, percentage = 0.3f), + aPollAnswerItem(answer = A_POLL_ANSWER_2, isSelected = true, isEnabled = false, isWinner = true, votesCount = 6, percentage = 0.6f), + aPollAnswerItem(answer = A_POLL_ANSWER_3, isEnabled = false), + aPollAnswerItem(answer = A_POLL_ANSWER_4, isEnabled = false, votesCount = 1, percentage = 0.1f), + ), + isEnded = true, + ) + assertThat(state).isEqualTo(expectedState) + } + + @Test + fun `Disclosed poll - ended, some votes, including one from current user (not winner) and two winning votes`() = runTest { + val votes = OTHER_WINNING_VOTES.mapKeys { it.key.id }.toImmutableMap() + val state = factory.create( + eventTimelineItem, aPollContent(votes = votes, endTime = 1UL) + ) + val expectedState = aPollContentState( + answerItems = listOf( + aPollAnswerItem(answer = A_POLL_ANSWER_1, isEnabled = false, isWinner = true, votesCount = 4, percentage = 0.4f), + aPollAnswerItem(answer = A_POLL_ANSWER_2, isSelected = true, isEnabled = false, votesCount = 2, percentage = 0.2f), + aPollAnswerItem(answer = A_POLL_ANSWER_3, isEnabled = false), + aPollAnswerItem(answer = A_POLL_ANSWER_4, isEnabled = false, isWinner = true, votesCount = 4, percentage = 0.4f), + ), + isEnded = true, + ) + assertThat(state).isEqualTo(expectedState) + } + + @Test + fun `Undisclosed poll - not ended, no votes`() = runTest { + val state = factory.create(eventTimelineItem, aPollContent(PollKind.Undisclosed)) + val expectedState = aPollContentState(pollKind = PollKind.Undisclosed).let { + it.copy( + answerItems = it.answerItems.map { answerItem -> answerItem.copy(isDisclosed = false) }.toImmutableList() + ) + } + assertThat(state).isEqualTo(expectedState) + } + + @Test + fun `Undisclosed poll - not ended, some votes, including one from current user`() = runTest { + val votes = MY_USER_WINNING_VOTES.mapKeys { it.key.id }.toImmutableMap() + val state = factory.create( + eventTimelineItem, aPollContent(PollKind.Undisclosed, votes = votes) + ) + val expectedState = aPollContentState( + pollKind = PollKind.Undisclosed, + answerItems = listOf( + aPollAnswerItem(answer = A_POLL_ANSWER_1, isDisclosed = false, votesCount = 3, percentage = 0.3f), + aPollAnswerItem(answer = A_POLL_ANSWER_2, isDisclosed = false, isSelected = true, votesCount = 6, percentage = 0.6f), + aPollAnswerItem(answer = A_POLL_ANSWER_3, isDisclosed = false), + aPollAnswerItem(answer = A_POLL_ANSWER_4, isDisclosed = false, votesCount = 1, percentage = 0.1f), + ), + ) + assertThat(state).isEqualTo(expectedState) + } + + @Test + fun `Undisclosed poll - ended, no votes, no winner`() = runTest { + val state = factory.create(eventTimelineItem, aPollContent(PollKind.Undisclosed, endTime = 1UL)) + val expectedState = aPollContentState( + isEnded = true, + pollKind = PollKind.Undisclosed + ).let { + it.copy( + answerItems = it.answerItems.map { answerItem -> answerItem.copy(isDisclosed = true, isEnabled = false) }.toImmutableList(), + ) + } + assertThat(state).isEqualTo(expectedState) + } + + @Test + fun `Undisclosed poll - ended, some votes, including one from current user (winner)`() = runTest { + val votes = MY_USER_WINNING_VOTES.mapKeys { it.key.id }.toImmutableMap() + val state = factory.create( + eventTimelineItem, aPollContent(PollKind.Undisclosed, votes = votes, endTime = 1UL) + ) + val expectedState = aPollContentState( + pollKind = PollKind.Undisclosed, + answerItems = listOf( + aPollAnswerItem(answer = A_POLL_ANSWER_1, isEnabled = false, votesCount = 3, percentage = 0.3f), + aPollAnswerItem(answer = A_POLL_ANSWER_2, isSelected = true, isEnabled = false, isWinner = true, votesCount = 6, percentage = 0.6f), + aPollAnswerItem(answer = A_POLL_ANSWER_3, isEnabled = false), + aPollAnswerItem(answer = A_POLL_ANSWER_4, isEnabled = false, votesCount = 1, percentage = 0.1f), + ), + isEnded = true, + ) + assertThat(state).isEqualTo(expectedState) + } + + @Test + fun `Undisclosed poll - ended, some votes, including one from current user (not winner) and two winning votes`() = runTest { + val votes = OTHER_WINNING_VOTES.mapKeys { it.key.id }.toImmutableMap() + val state = factory.create( + eventTimelineItem, aPollContent(PollKind.Undisclosed, votes = votes, endTime = 1UL) + ) + val expectedState = aPollContentState( + pollKind = PollKind.Undisclosed, + answerItems = listOf( + aPollAnswerItem(answer = A_POLL_ANSWER_1, isEnabled = false, isWinner = true, votesCount = 4, percentage = 0.4f), + aPollAnswerItem(answer = A_POLL_ANSWER_2, isSelected = true, isEnabled = false, votesCount = 2, percentage = 0.2f), + aPollAnswerItem(answer = A_POLL_ANSWER_3, isEnabled = false), + aPollAnswerItem(answer = A_POLL_ANSWER_4, isEnabled = false, isWinner = true, votesCount = 4, percentage = 0.4f), + ), + isEnded = true, + ) + assertThat(state).isEqualTo(expectedState) + } + + @Test + fun `eventId is populated`() = runTest { + val state = factory.create(eventTimelineItem, aPollContent()) + assertThat(state.eventId).isEqualTo(eventTimelineItem.eventId) + } + + private fun aPollContent( + pollKind: PollKind = PollKind.Disclosed, + votes: ImmutableMap> = persistentMapOf(), + endTime: ULong? = null, + ): PollContent = PollContent( + question = A_POLL_QUESTION, + kind = pollKind, + maxSelections = 1UL, + answers = persistentListOf(A_POLL_ANSWER_1, A_POLL_ANSWER_2, A_POLL_ANSWER_3, A_POLL_ANSWER_4), + votes = votes, + endTime = endTime, + isEdited = false, + ) + + private fun aPollContentState( + eventId: EventId? = AN_EVENT_ID, + pollKind: PollKind = PollKind.Disclosed, + answerItems: List = listOf( + aPollAnswerItem(A_POLL_ANSWER_1), + aPollAnswerItem(A_POLL_ANSWER_2), + aPollAnswerItem(A_POLL_ANSWER_3), + aPollAnswerItem(A_POLL_ANSWER_4), + ), + isEnded: Boolean = false, + isMine: Boolean = false, + isEditable: Boolean = false, + question: String = A_POLL_QUESTION, + ) = PollContentState( + eventId = eventId, + question = question, + answerItems = answerItems.toImmutableList(), + pollKind = pollKind, + isPollEditable = isEditable, + isPollEnded = isEnded, + isMine = isMine, + ) + + private fun aPollAnswerItem( + answer: PollAnswer, + isSelected: Boolean = false, + isEnabled: Boolean = true, + isWinner: Boolean = false, + isDisclosed: Boolean = true, + votesCount: Int = 0, + percentage: Float = 0f, + ) = PollAnswerItem( + answer = answer, + isSelected = isSelected, + isEnabled = isEnabled, + isWinner = isWinner, + isDisclosed = isDisclosed, + votesCount = votesCount, + percentage = percentage, + ) + + private companion object TestData { + private const val A_POLL_QUESTION = "What is your favorite food?" + private val A_POLL_ANSWER_1 = PollAnswer("id_1", "Pizza") + private val A_POLL_ANSWER_2 = PollAnswer("id_2", "Pasta") + private val A_POLL_ANSWER_3 = PollAnswer("id_3", "French Fries") + private val A_POLL_ANSWER_4 = PollAnswer("id_4", "Hamburger") + + private val MY_USER_WINNING_VOTES = persistentMapOf( + A_POLL_ANSWER_1 to persistentListOf(A_USER_ID_2, A_USER_ID_3, A_USER_ID_4), + A_POLL_ANSWER_2 to persistentListOf(A_USER_ID /* my vote */, A_USER_ID_5, A_USER_ID_6, A_USER_ID_7, A_USER_ID_8, A_USER_ID_9), // winner + A_POLL_ANSWER_3 to persistentListOf(), + A_POLL_ANSWER_4 to persistentListOf(A_USER_ID_10), + ) + private val OTHER_WINNING_VOTES = persistentMapOf( + A_POLL_ANSWER_1 to persistentListOf(A_USER_ID_2, A_USER_ID_3, A_USER_ID_4, A_USER_ID_5), // winner + A_POLL_ANSWER_2 to persistentListOf(A_USER_ID /* my vote */, A_USER_ID_6), + A_POLL_ANSWER_3 to persistentListOf(), + A_POLL_ANSWER_4 to persistentListOf(A_USER_ID_7, A_USER_ID_8, A_USER_ID_9, A_USER_ID_10), // winner + ) + } +} diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/ReportType.kt b/features/poll/test/build.gradle.kts similarity index 68% rename from features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/ReportType.kt rename to features/poll/test/build.gradle.kts index 17b75ea1a7..2bae89d155 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/ReportType.kt +++ b/features/poll/test/build.gradle.kts @@ -14,13 +14,16 @@ * limitations under the License. */ -package io.element.android.features.rageshake.api.reporter - -enum class ReportType { - BUG_REPORT, - SUGGESTION, - SPACE_BETA_FEEDBACK, - THREADS_BETA_FEEDBACK, - AUTO_UISI, - AUTO_UISI_SENDER, +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.features.poll.test" +} + +dependencies { + implementation(projects.libraries.matrix.api) + api(projects.features.poll.api) + implementation(libs.kotlinx.collections.immutable) } diff --git a/features/poll/test/src/main/kotlin/io/element/android/features/poll/test/actions/FakeEndPollAction.kt b/features/poll/test/src/main/kotlin/io/element/android/features/poll/test/actions/FakeEndPollAction.kt new file mode 100644 index 0000000000..285db277e4 --- /dev/null +++ b/features/poll/test/src/main/kotlin/io/element/android/features/poll/test/actions/FakeEndPollAction.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.test.actions + +import io.element.android.features.poll.api.actions.EndPollAction +import io.element.android.libraries.matrix.api.core.EventId + +class FakeEndPollAction : EndPollAction { + + private var executionCount = 0 + + fun verifyExecutionCount(count: Int) { + assert(executionCount == count) + } + + override suspend fun execute(pollStartId: EventId): Result { + executionCount++ + return Result.success(Unit) + } +} diff --git a/features/poll/test/src/main/kotlin/io/element/android/features/poll/test/actions/FakeSendPollResponseAction.kt b/features/poll/test/src/main/kotlin/io/element/android/features/poll/test/actions/FakeSendPollResponseAction.kt new file mode 100644 index 0000000000..f8fa3316d9 --- /dev/null +++ b/features/poll/test/src/main/kotlin/io/element/android/features/poll/test/actions/FakeSendPollResponseAction.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.test.actions + +import io.element.android.features.poll.api.actions.SendPollResponseAction +import io.element.android.libraries.matrix.api.core.EventId + +class FakeSendPollResponseAction : SendPollResponseAction { + + private var executionCount = 0 + + fun verifyExecutionCount(count: Int) { + assert(executionCount == count) + } + + override suspend fun execute(pollStartId: EventId, answerId: String): Result { + executionCount++ + return Result.success(Unit) + } +} diff --git a/features/poll/test/src/main/kotlin/io/element/android/features/poll/test/pollcontent/FakePollContentStateFactory.kt b/features/poll/test/src/main/kotlin/io/element/android/features/poll/test/pollcontent/FakePollContentStateFactory.kt new file mode 100644 index 0000000000..b8377afe46 --- /dev/null +++ b/features/poll/test/src/main/kotlin/io/element/android/features/poll/test/pollcontent/FakePollContentStateFactory.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.poll.test.pollcontent + +import io.element.android.features.poll.api.pollcontent.PollAnswerItem +import io.element.android.features.poll.api.pollcontent.PollContentState +import io.element.android.features.poll.api.pollcontent.PollContentStateFactory +import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +import kotlinx.collections.immutable.toImmutableList + +class FakePollContentStateFactory : PollContentStateFactory { + + override suspend fun create(event: EventTimelineItem, content: PollContent): PollContentState { + return PollContentState( + eventId = event.eventId, + question = content.question, + answerItems = emptyList().toImmutableList(), + pollKind = content.kind, + isPollEditable = event.isEditable, + isPollEnded = content.endTime != null, + isMine = event.isOwn + ) + } +} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt index bd7d961123..cab79b61e4 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt @@ -19,7 +19,6 @@ package io.element.android.features.preferences.impl import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin @@ -41,8 +40,8 @@ import io.element.android.features.preferences.impl.notifications.NotificationSe import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingNode import io.element.android.features.preferences.impl.root.PreferencesRootNode import io.element.android.features.preferences.impl.user.editprofile.EditUserProfileNode -import io.element.android.libraries.architecture.BackstackNode -import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler +import io.element.android.libraries.architecture.BackstackView +import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId @@ -55,7 +54,7 @@ class PreferencesFlowNode @AssistedInject constructor( @Assisted plugins: List, private val lockScreenEntryPoint: LockScreenEntryPoint, private val logoutEntryPoint: LogoutEntryPoint, -) : BackstackNode( +) : BaseFlowNode( backstack = BackStack( initialElement = plugins.filterIsInstance().first().initialElement.toNavTarget(), savedStateMap = buildContext.savedStateMap, @@ -210,10 +209,6 @@ class PreferencesFlowNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { - Children( - navModel = backstack, - modifier = modifier, - transitionHandler = rememberDefaultTransitionHandler() - ) + BackstackView() } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutNode.kt index 000a397cfe..423ae6c793 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutNode.kt @@ -28,7 +28,7 @@ import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @ContributesNode(SessionScope::class) class AboutNode @AssistedInject constructor( diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt index f79c53335c..f22a653f6d 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt @@ -16,7 +16,7 @@ package io.element.android.features.preferences.impl.advanced -import io.element.android.libraries.theme.theme.Theme +import io.element.android.compound.theme.Theme sealed interface AdvancedSettingsEvents { data class SetRichTextEditorEnabled(val enabled: Boolean) : AdvancedSettingsEvents diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt index bf438db4ef..46a4bd0bbd 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt @@ -23,10 +23,10 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue +import io.element.android.compound.theme.Theme +import io.element.android.compound.theme.mapToTheme import io.element.android.features.preferences.api.store.PreferencesStore import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.theme.theme.Theme -import io.element.android.libraries.theme.theme.mapToTheme import kotlinx.coroutines.launch import javax.inject.Inject diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt index 8258684750..01d702224f 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt @@ -16,7 +16,7 @@ package io.element.android.features.preferences.impl.advanced -import io.element.android.libraries.theme.theme.Theme +import io.element.android.compound.theme.Theme data class AdvancedSettingsState( val isRichTextEditorEnabled: Boolean, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt index 012a39896c..aadf27dd20 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt @@ -17,7 +17,7 @@ package io.element.android.features.preferences.impl.advanced import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.libraries.theme.theme.Theme +import io.element.android.compound.theme.Theme open class AdvancedSettingsStateProvider : PreviewParameterProvider { override val values: Sequence diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt index e757c79236..011500a739 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt @@ -25,13 +25,12 @@ import io.element.android.libraries.designsystem.components.dialogs.ListOption import io.element.android.libraries.designsystem.components.dialogs.SingleSelectionDialog import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.preferences.PreferencePage -import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.ListItem import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.theme.Theme -import io.element.android.libraries.theme.theme.themes +import io.element.android.compound.theme.Theme +import io.element.android.compound.theme.themes import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList @@ -49,9 +48,7 @@ fun AdvancedSettingsView( ) { ListItem( headlineContent = { - Text( - text = stringResource(id = CommonStrings.common_appearance) - ) + Text(text = stringResource(id = CommonStrings.common_appearance)) }, trailingContent = ListItemContent.Text( state.theme.toHumanReadable() @@ -60,17 +57,29 @@ fun AdvancedSettingsView( state.eventSink(AdvancedSettingsEvents.ChangeTheme) } ) - PreferenceSwitch( - title = stringResource(id = CommonStrings.common_rich_text_editor), - subtitle = stringResource(id = R.string.screen_advanced_settings_rich_text_editor_description), - isChecked = state.isRichTextEditorEnabled, - onCheckedChange = { state.eventSink(AdvancedSettingsEvents.SetRichTextEditorEnabled(it)) }, + ListItem( + headlineContent = { + Text(text = stringResource(id = CommonStrings.common_rich_text_editor)) + }, + supportingContent = { + Text(text = stringResource(id = R.string.screen_advanced_settings_rich_text_editor_description)) + }, + trailingContent = ListItemContent.Switch( + checked = state.isRichTextEditorEnabled, + onChange = { state.eventSink(AdvancedSettingsEvents.SetRichTextEditorEnabled(it)) }, + ), ) - PreferenceSwitch( - title = stringResource(id = R.string.screen_advanced_settings_developer_mode), - subtitle = stringResource(id = R.string.screen_advanced_settings_developer_mode_description), - isChecked = state.isDeveloperModeEnabled, - onCheckedChange = { state.eventSink(AdvancedSettingsEvents.SetDeveloperModeEnabled(it)) }, + ListItem( + headlineContent = { + Text(text = stringResource(id = CommonStrings.action_view_source)) + }, + supportingContent = { + Text(text = stringResource(id = R.string.screen_advanced_settings_view_source_description)) + }, + trailingContent = ListItemContent.Switch( + checked = state.isDeveloperModeEnabled, + onChange = { state.eventSink(AdvancedSettingsEvents.SetDeveloperModeEnabled(it)) }, + ), ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt index ae6c70188b..df0ea43ce9 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt @@ -75,10 +75,12 @@ class DeveloperSettingsPresenter @Inject constructor( .collectAsState(initial = null) LaunchedEffect(Unit) { - FeatureFlags.entries.forEach { feature -> - features[feature.key] = feature - enabledFeatures[feature.key] = featureFlagService.isFeatureEnabled(feature) - } + FeatureFlags.entries + .filter { it.isFinished.not() } + .forEach { feature -> + features[feature.key] = feature + enabledFeatures[feature.key] = featureFlagService.isFeatureEnabled(feature) + } } val featureUiModels = createUiModels(features, enabledFeatures) val coroutineScope = rememberCoroutineScope() diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingView.kt index 15fd6e46b7..71ff1c3d58 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingView.kt @@ -39,10 +39,12 @@ import androidx.compose.ui.draw.rotate import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.list.ListItemContent -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.DropdownMenu import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem @@ -52,10 +54,8 @@ import io.element.android.libraries.designsystem.theme.components.ListItem import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.tracing.LogLevel import io.element.android.libraries.matrix.api.tracing.Target -import io.element.android.libraries.theme.ElementTheme import kotlinx.collections.immutable.ImmutableMap @OptIn(ExperimentalMaterial3Api::class) @@ -90,7 +90,7 @@ fun ConfigureTracingView( onClick = { showMenu = !showMenu } ) { Icon( - resourceId = CommonDrawables.ic_compound_overflow_vertical, + imageVector = CompoundIcons.OverflowVertical, tint = ElementTheme.materialColors.secondary, contentDescription = null, ) @@ -107,7 +107,7 @@ fun ConfigureTracingView( text = { Text("Reset to default") }, leadingIcon = { Icon( - resourceId = CommonDrawables.ic_compound_delete, + imageVector = CompoundIcons.Delete, tint = ElementTheme.materialColors.secondary, contentDescription = null, ) @@ -211,7 +211,7 @@ private fun LogLevelDropdownMenu( trailingIcon = { Icon( modifier = Modifier.rotate(if (expanded) 180f else 0f), - resourceId = CommonDrawables.ic_compound_chevron_down, + imageVector = CompoundIcons.ChevronDown, contentDescription = null, ) }, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt index 9e87675b3a..f38c211b67 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt @@ -17,11 +17,11 @@ package io.element.android.features.preferences.impl.notifications sealed interface NotificationSettingsEvents { - data object RefreshSystemNotificationsEnabled : NotificationSettingsEvents data class SetNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents data class SetAtRoomNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents data class SetCallNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents + data class SetInviteForMeNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents data object FixConfigurationMismatch : NotificationSettingsEvents data object ClearConfigurationMismatchError : NotificationSettingsEvents data object ClearNotificationChangeError : NotificationSettingsEvents diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt index 689cca8f66..6252be1e5c 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt @@ -76,6 +76,9 @@ class NotificationSettingsPresenter @Inject constructor( is NotificationSettingsEvents.SetCallNotificationsEnabled -> { localCoroutineScope.setCallNotificationsEnabled(event.enabled, changeNotificationSettingAction) } + is NotificationSettingsEvents.SetInviteForMeNotificationsEnabled -> { + localCoroutineScope.setInviteForMeNotificationsEnabled(event.enabled, changeNotificationSettingAction) + } is NotificationSettingsEvents.SetNotificationsEnabled -> localCoroutineScope.setNotificationsEnabled(userPushStore, event.enabled) NotificationSettingsEvents.ClearConfigurationMismatchError -> { matrixSettings.value = NotificationSettingsState.MatrixSettings.Invalid(fixFailed = false) @@ -123,10 +126,12 @@ class NotificationSettingsPresenter @Inject constructor( val callNotificationsEnabled = notificationSettingsService.isCallEnabled().getOrThrow() val atRoomNotificationsEnabled = notificationSettingsService.isRoomMentionEnabled().getOrThrow() + val inviteForMeNotificationsEnabled = notificationSettingsService.isInviteForMeEnabled().getOrThrow() target.value = NotificationSettingsState.MatrixSettings.Valid( atRoomNotificationsEnabled = atRoomNotificationsEnabled, callNotificationsEnabled = callNotificationsEnabled, + inviteForMeNotificationsEnabled = inviteForMeNotificationsEnabled, defaultGroupNotificationMode = encryptedGroupDefaultMode, defaultOneToOneNotificationMode = encryptedOneToOneDefaultMode, ) @@ -175,6 +180,12 @@ class NotificationSettingsPresenter @Inject constructor( }.runCatchingUpdatingState(action) } + private fun CoroutineScope.setInviteForMeNotificationsEnabled(enabled: Boolean, action: MutableState>) = launch { + suspend { + notificationSettingsService.setInviteForMeEnabled(enabled).getOrThrow() + }.runCatchingUpdatingState(action) + } + private fun CoroutineScope.setNotificationsEnabled(userPushStore: UserPushStore, enabled: Boolean) = launch { userPushStore.setNotificationEnabledForDevice(enabled) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt index 2b0faa110c..581f0e8475 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt @@ -32,6 +32,7 @@ data class NotificationSettingsState( data class Valid( val atRoomNotificationsEnabled: Boolean, val callNotificationsEnabled: Boolean, + val inviteForMeNotificationsEnabled: Boolean, val defaultGroupNotificationMode: RoomNotificationMode?, val defaultOneToOneNotificationMode: RoomNotificationMode?, ) : MatrixSettings diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt index 9336857585..f121a2fe60 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt @@ -35,6 +35,7 @@ fun aNotificationSettingsState( matrixSettings = NotificationSettingsState.MatrixSettings.Valid( atRoomNotificationsEnabled = true, callNotificationsEnabled = true, + inviteForMeNotificationsEnabled = true, defaultGroupNotificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY, defaultOneToOneNotificationMode = RoomNotificationMode.ALL_MESSAGES, ), diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt index 167dc9b1c1..f1066b2e9d 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt @@ -23,6 +23,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.lifecycle.Lifecycle +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.preferences.impl.R import io.element.android.libraries.androidutils.system.startNotificationSettingsIntent import io.element.android.libraries.designsystem.atomic.molecules.DialogLikeBannerMolecule @@ -34,7 +35,6 @@ import io.element.android.libraries.designsystem.components.preferences.Preferen import io.element.android.libraries.designsystem.components.preferences.PreferenceText import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.ui.strings.CommonStrings @@ -77,6 +77,7 @@ fun NotificationSettingsView( onMentionNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(it)) }, // TODO We are removing the call notification toggle until support for call notifications has been added // onCallsNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetCallNotificationsEnabled(it)) }, + onInviteForMeNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetInviteForMeNotificationsEnabled(it)) }, ) } AsyncView( @@ -98,12 +99,13 @@ private fun NotificationSettingsContentView( onMentionNotificationsChanged: (Boolean) -> Unit, // TODO We are removing the call notification toggle until support for call notifications has been added // onCallsNotificationsChanged: (Boolean) -> Unit, + onInviteForMeNotificationsChanged: (Boolean) -> Unit, modifier: Modifier = Modifier, ) { val context = LocalContext.current if (systemSettings.appNotificationsEnabled && !systemSettings.systemNotificationsEnabled) { PreferenceText( - iconResourceId = CommonDrawables.ic_compound_notifications_solid_off, + icon = CompoundIcons.NotificationsSolidOff, title = stringResource(id = R.string.screen_notification_settings_system_notifications_turned_off), subtitle = stringResource( id = R.string.screen_notification_settings_system_notifications_action_required, @@ -147,8 +149,8 @@ private fun NotificationSettingsContentView( onCheckedChange = onMentionNotificationsChanged ) } - // TODO We are removing the call notification toggle until support for call notifications has been added -// PreferenceCategory(title = stringResource(id = CommonStrings.screen_notification_settings_additional_settings_section_title)) { + PreferenceCategory(title = stringResource(id = R.string.screen_notification_settings_additional_settings_section_title)) { + // TODO We are removing the call notification toggle until support for call notifications has been added // PreferenceSwitch( // modifier = Modifier, // title = stringResource(id = CommonStrings.screen_notification_settings_calls_label), @@ -156,7 +158,14 @@ private fun NotificationSettingsContentView( // switchAlignment = Alignment.Top, // onCheckedChange = onCallsNotificationsChanged // ) -// } + PreferenceSwitch( + modifier = Modifier, + title = stringResource(id = R.string.screen_notification_settings_invite_for_me_label), + isChecked = matrixSettings.inviteForMeNotificationsEnabled, + switchAlignment = Alignment.Top, + onCheckedChange = onInviteForMeNotificationsChanged + ) + } } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/DefaultNotificationSettingOption.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/DefaultNotificationSettingOption.kt index 3201243e75..1d07589a24 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/DefaultNotificationSettingOption.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/DefaultNotificationSettingOption.kt @@ -16,67 +16,43 @@ package io.element.android.features.preferences.impl.notifications.edit import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.selection.selectable import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.Role -import androidx.compose.ui.unit.dp import io.element.android.features.preferences.impl.R import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.theme.components.RadioButton import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.room.RoomNotificationMode -import io.element.android.libraries.theme.ElementTheme +import io.element.android.libraries.designsystem.components.list.ListItemContent +import io.element.android.libraries.designsystem.theme.components.ListItem @Composable fun DefaultNotificationSettingOption( mode: RoomNotificationMode, + onOptionSelected: (RoomNotificationMode) -> Unit, + displayMentionsOnlyDisclaimer: Boolean, modifier: Modifier = Modifier, isSelected: Boolean = false, - onOptionSelected: (RoomNotificationMode) -> Unit = {}, ) { - val subtitle = when(mode) { + val title = when (mode) { RoomNotificationMode.ALL_MESSAGES -> stringResource(id = R.string.screen_notification_settings_edit_mode_all_messages) RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> stringResource(id = R.string.screen_notification_settings_edit_mode_mentions_and_keywords) else -> "" } - Row( - modifier - .fillMaxWidth() - .selectable( - selected = isSelected, - onClick = { onOptionSelected(mode) }, - role = Role.RadioButton, - ) - .padding(8.dp), - ) { - Column( - Modifier - .weight(1f) - .padding(horizontal = 8.dp) - .align(Alignment.CenterVertically) - ) { - Text( - text = subtitle, - style = ElementTheme.typography.fontBodyLgRegular, - ) + val subtitle = when { + mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY && displayMentionsOnlyDisclaimer -> { + stringResource(id = R.string.screen_notification_settings_mentions_only_disclaimer) } - - RadioButton( - modifier = Modifier - .align(Alignment.CenterVertically) - .size(48.dp), - selected = isSelected, - onClick = null // null recommended for accessibility with screenreaders - ) + else -> null } + ListItem( + modifier = modifier, + headlineContent = { Text(title) }, + supportingContent = subtitle?.let { { Text(it) } }, + trailingContent = ListItemContent.RadioButton(selected = isSelected), + onClick = { onOptionSelected(mode) }, + ) } @PreviewsDayNight @@ -86,10 +62,20 @@ internal fun DefaultNotificationSettingOptionPreview() = ElementPreview { DefaultNotificationSettingOption( mode = RoomNotificationMode.ALL_MESSAGES, isSelected = true, + displayMentionsOnlyDisclaimer = false, + onOptionSelected = {}, ) DefaultNotificationSettingOption( mode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY, isSelected = false, + displayMentionsOnlyDisclaimer = false, + onOptionSelected = {}, + ) + DefaultNotificationSettingOption( + mode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY, + isSelected = false, + displayMentionsOnlyDisclaimer = true, + onOptionSelected = {}, ) } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt index 79201e27d3..7617e68dd6 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt @@ -19,9 +19,11 @@ package io.element.android.features.preferences.impl.notifications.edit import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -33,6 +35,7 @@ import io.element.android.libraries.matrix.api.notificationsettings.Notification import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.RoomSummary +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.debounce @@ -54,6 +57,7 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor( } @Composable override fun present(): EditDefaultNotificationSettingState { + var displayMentionsOnlyDisclaimer by remember { mutableStateOf(false) } val mode: MutableState = remember { mutableStateOf(null) @@ -70,6 +74,7 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor( fetchSettings(mode) observeNotificationSettings(mode) observeRoomSummaries(roomsWithUserDefinedMode) + displayMentionsOnlyDisclaimer = !notificationSettingsService.canHomeServerPushEncryptedEventsToDevice().getOrDefault(true) } fun handleEvents(event: EditDefaultNotificationSettingStateEvents) { @@ -84,8 +89,9 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor( return EditDefaultNotificationSettingState( isOneToOne = isOneToOne, mode = mode.value, - roomsWithUserDefinedMode = roomsWithUserDefinedMode.value, + roomsWithUserDefinedMode = roomsWithUserDefinedMode.value.toImmutableList(), changeNotificationSettingAction = changeNotificationSettingAction.value, + displayMentionsOnlyDisclaimer = displayMentionsOnlyDisclaimer, eventSink = ::handleEvents ) } @@ -105,7 +111,7 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor( } private fun CoroutineScope.observeRoomSummaries(roomsWithUserDefinedMode: MutableState>) { - roomListService.allRooms() + roomListService.allRooms .summaries .onEach { updateRoomsWithUserDefinedMode(it, roomsWithUserDefinedMode) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingState.kt index e8590ec27f..7086835478 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingState.kt @@ -19,11 +19,13 @@ package io.element.android.features.preferences.impl.notifications.edit import io.element.android.libraries.architecture.Async import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.roomlist.RoomSummary +import kotlinx.collections.immutable.ImmutableList data class EditDefaultNotificationSettingState( val isOneToOne: Boolean, val mode: RoomNotificationMode?, - val roomsWithUserDefinedMode: List, + val roomsWithUserDefinedMode: ImmutableList, val changeNotificationSettingAction: Async, + val displayMentionsOnlyDisclaimer: Boolean, val eventSink: (EditDefaultNotificationSettingStateEvents) -> Unit, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateProvider.kt index 71fbfb1e1b..dc11b85e6d 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateProvider.kt @@ -22,6 +22,7 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.roomlist.RoomSummary import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails +import kotlinx.collections.immutable.persistentListOf open class EditDefaultNotificationSettingStateProvider: PreviewParameterProvider { override val values: Sequence @@ -30,17 +31,20 @@ open class EditDefaultNotificationSettingStateProvider: PreviewParameterProvider anEditDefaultNotificationSettingsState(isOneToOne = true), anEditDefaultNotificationSettingsState(changeNotificationSettingAction = Async.Loading(Unit)), anEditDefaultNotificationSettingsState(changeNotificationSettingAction = Async.Failure(Throwable("error"))), + anEditDefaultNotificationSettingsState(displayMentionsOnlyDisclaimer = true), ) } private fun anEditDefaultNotificationSettingsState( isOneToOne: Boolean = false, - changeNotificationSettingAction: Async = Async.Uninitialized + changeNotificationSettingAction: Async = Async.Uninitialized, + displayMentionsOnlyDisclaimer: Boolean = false, ) = EditDefaultNotificationSettingState( isOneToOne = isOneToOne, mode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY, - roomsWithUserDefinedMode = listOf(aRoomSummary()), + roomsWithUserDefinedMode = persistentListOf(aRoomSummary()), changeNotificationSettingAction = changeNotificationSettingAction, + displayMentionsOnlyDisclaimer = displayMentionsOnlyDisclaimer, eventSink = {} ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt index 415d5e01da..7b619a7322 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt @@ -76,6 +76,7 @@ fun EditDefaultNotificationSettingView( DefaultNotificationSettingOption( mode = item, isSelected = state.mode == item, + displayMentionsOnlyDisclaimer = state.displayMentionsOnlyDisclaimer, onOptionSelected = { state.eventSink(EditDefaultNotificationSettingStateEvents.SetNotificationMode(it)) } ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt index 43ceb427b9..ecb2f7f070 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt @@ -30,7 +30,7 @@ import io.element.android.anvilannotations.ContributesNode import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.user.MatrixUser -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @ContributesNode(SessionScope::class) class PreferencesRootNode @AssistedInject constructor( diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt index 4fa20bde8f..230648a96a 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt @@ -27,6 +27,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.preferences.impl.R import io.element.android.features.preferences.impl.user.UserPreferences import io.element.android.libraries.designsystem.components.list.ListItemContent @@ -44,7 +46,6 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.components.MatrixUserProvider -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -83,7 +84,7 @@ fun PreferencesRootView( if (state.showCompleteVerification) { ListItem( headlineContent = { Text(text = stringResource(CommonStrings.common_verify_device)) }, - leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_check_circle)), + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.CheckCircle)), onClick = onVerifyClicked ) } @@ -101,8 +102,8 @@ fun PreferencesRootView( if (state.accountManagementUrl != null) { ListItem( headlineContent = { Text(stringResource(id = CommonStrings.action_manage_account)) }, - leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_user)), - trailingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_pop_out)), + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.UserProfile)), + trailingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.PopOut)), onClick = { onManageAccountClicked(state.accountManagementUrl) }, ) HorizontalDivider() @@ -117,18 +118,18 @@ fun PreferencesRootView( if (state.showNotificationSettings) { ListItem( headlineContent = { Text(stringResource(id = R.string.screen_notification_settings_title)) }, - leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_notifications)), + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Notifications)), onClick = onOpenNotificationSettings, ) } ListItem( headlineContent = { Text(stringResource(id = CommonStrings.common_report_a_problem)) }, - leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_chat_problem)), + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.ChatProblem)), onClick = onOpenRageShake ) ListItem( headlineContent = { Text(stringResource(id = CommonStrings.common_about)) }, - leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_info)), + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Info)), onClick = onOpenAbout, ) if (state.showLockScreenSettings) { @@ -143,14 +144,14 @@ fun PreferencesRootView( ListItem( headlineContent = { Text(stringResource(id = CommonStrings.action_manage_devices)) }, leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_devices)), - trailingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_pop_out)), + trailingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.PopOut)), onClick = { onManageAccountClicked(state.devicesManagementUrl) }, ) HorizontalDivider() } ListItem( headlineContent = { Text(stringResource(id = CommonStrings.common_advanced_settings)) }, - leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_settings)), + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Settings)), onClick = onOpenAdvancedSettings, ) if (state.showDeveloperSettings) { diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt index 9031a2e20f..66aaeb6c40 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt @@ -56,7 +56,7 @@ import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet import io.element.android.libraries.matrix.ui.components.EditableAvatarView import io.element.android.libraries.permissions.api.PermissionsView -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.launch diff --git a/features/preferences/impl/src/main/res/values-cs/translations.xml b/features/preferences/impl/src/main/res/values-cs/translations.xml index 49ffdd14a1..76d764c131 100644 --- a/features/preferences/impl/src/main/res/values-cs/translations.xml +++ b/features/preferences/impl/src/main/res/values-cs/translations.xml @@ -6,6 +6,7 @@ "Vývojářský režim" "Povolením získáte přístup k funkcím a funkcím pro vývojáře." "Vypněte editor formátovaného textu pro ruční zadání Markdown." + "Povolit možnost zobrazení zdroje zprávy na časové ose." "Zobrazované jméno" "Vaše zobrazované jméno" "Došlo k neznámé chybě a informace nelze změnit." @@ -30,6 +31,8 @@ Pokud budete pokračovat, některá nastavení se mohou změnit." "Povolit oznámení na tomto zařízení" "Konfigurace nebyla opravena, zkuste to prosím znovu." "Skupinové chaty" + "Pozvánky" + "Váš domovský server tuto možnost v zašifrovaných místnostech nepodporuje, v některých místnostech nemusíte být upozorněni." "Zmínky" "Vše" "Zmínky" diff --git a/features/preferences/impl/src/main/res/values-de/translations.xml b/features/preferences/impl/src/main/res/values-de/translations.xml index 4287d4d7c8..cacc15a633 100644 --- a/features/preferences/impl/src/main/res/values-de/translations.xml +++ b/features/preferences/impl/src/main/res/values-de/translations.xml @@ -1,7 +1,12 @@ + "Benutzerdefinierte Element-Aufruf-Basis-URL" + "Lege eine eigene Basis-URL für Element Call fest." + "Ungültige URL, bitte stelle sicher, dass du das Protokoll (http/https) und die richtige Adresse angibst." "Entwickler-Modus" + "Aktivieren, um Zugriff auf Features und Funktionen für Entwickler zu aktivieren." "Deaktiviere den Rich-Text-Editor, um Markdown manuell einzugeben." + "Option aktiveren, um Nachrichtenquelle in der Zeitleiste anzuzeigen." "Anzeigename" "Dein Anzeigename" "Ein unbekannter Fehler ist aufgetreten und die Informationen konnten nicht geändert werden." @@ -22,6 +27,7 @@ "Benachrichtigungen auf diesem Gerät aktivieren" "Die Konfiguration wurde nicht korrigiert, bitte versuche es erneut." "Gruppenchats" + "Dein Homeserver unterstützt diese Option in verschlüsselten Räumen nicht. In einigen Räumen wirst du möglicherweise nicht benachrichtigt." "Erwähnungen" "Alle" "Erwähnungen" diff --git a/features/preferences/impl/src/main/res/values-fr/translations.xml b/features/preferences/impl/src/main/res/values-fr/translations.xml index a831656698..dc5e6ff790 100644 --- a/features/preferences/impl/src/main/res/values-fr/translations.xml +++ b/features/preferences/impl/src/main/res/values-fr/translations.xml @@ -6,6 +6,7 @@ "Mode développeur" "Activer pour pouvoir accéder aux fonctionnalités destinées aux développeurs." "Désactivez l’éditeur de texte enrichi pour saisir manuellement du Markdown." + "Activer cette option pour pouvoir voir la source des messages dans la discussion." "Pseudonyme" "Votre pseudonyme" "Une erreur inconnue s’est produite et les informations n’ont pas pu être modifiées." @@ -30,6 +31,8 @@ Si vous continuez, il est possible que certains de vos paramètres soient modifi "Activer les notifications sur cet appareil" "La configuration n’a pas été corrigée, veuillez réessayer." "Discussions de groupe" + "Invitations" + "Votre serveur d’accueil ne supporte pas cette option pour les salons chiffrés, vous pourriez ne pas être notifié(e) dans certains salons." "Mentions" "Tous" "Mentions" diff --git a/features/preferences/impl/src/main/res/values-hu/translations.xml b/features/preferences/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..d5286dc323 --- /dev/null +++ b/features/preferences/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,43 @@ + + + "Egyéni Element Call alapwebcím" + "Egyéni alapwebcím beállítása az Element Callhoz." + "Érvénytelen webcím, győződjön meg arról, hogy szerepel benne a protokoll (http/https), és hogy helyes a cím." + "Fejlesztői mód" + "Engedélyezze, hogy elérje a fejlesztőknek szánt funkciókat." + "A formázott szöveges szerkesztő letiltása, hogy kézzel írhasson Markdownt." + "Engedélyezze a beállítást az üzenet forrásának megjelenítéséhez az idővonalon." + "Megjelenítendő név" + "Saját megjelenítendő név" + "Ismeretlen hiba történt, és az információ módosítása nem sikerült." + "Nem sikerült frissíteni a profilt" + "Profil szerkesztése" + "Profil frissítése…" + "További beállítások" + "Hang- és videóhívások" + "Konfigurációs eltérés" + "Egyszerűsítettük az értesítési beállításokat, hogy könnyebben megtalálhatók legyenek a lehetőségek. A korábban kiválasztott egyéni beállítások némelyike nem jelenik meg itt, de továbbra is aktív. + +Ha folytatja, egyes beállítások megváltozhatnak." + "Közvetlen csevegések" + "Egyéni beállítás csevegésenként" + "Hiba történt az értesítési beállítás frissítésekor." + "Összes üzenet" + "Csak említések és kulcsszavak" + "Közvetlen csevegéseknél értesítés ezekről:" + "Csoportos csevegésekben értesítés ezekről:" + "Értesítések engedélyezése ezen az eszközön" + "A konfiguráció nem lett kijavítva, próbálja újra." + "Csoportos csevegések" + "Meghívók" + "A Matrix-kiszolgálója nem támogatja ezt a beállítást a titkosított szobákban, előfordulhat, hogy egyes szobákban nem kap értesítést." + "Említések" + "Összes" + "Említések" + "Értesítés ezekről:" + "Értesítés a @room említésekor" + "Az értesítések fogadásához kérjük, módosítsa a %1$s." + "rendszerbeállításokat" + "A rendszerértesítések ki vannak kapcsolva" + "Értesítések" + diff --git a/features/preferences/impl/src/main/res/values-in/translations.xml b/features/preferences/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..f33bf54cfb --- /dev/null +++ b/features/preferences/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,45 @@ + + + "URL dasar Element Call khusus" + "Tetapkan URL dasar khusus untuk Element Call." + "URL tidak valid, pastikan Anda menyertakan protokol (http/https) dan alamat yang benar." + "Mode pengembang" + "Aktifkan untuk mengakses fitur dan fungsi untuk para pengembang." + "Nonaktifkan penyunting teks kaya untuk mengetik Markdown secara manual." + "Aktifkan opsi untuk melihat sumber pesan dalam lini masa." + "Nama tampilan" + "Nama tampilan Anda" + "Terjadi kesalahan yang tidak diketahui dan informasi tidak dapat diubah." + "Tidak dapat memperbarui profil" + "Sunting profil" + "Memperbarui profil…" + "Pengaturan tambahan" + "Panggilan audio dan video" + "Ketidakcocokan pengaturan" + "Kami telah menyederhanakan Pengaturan Pemberitahuan untuk membuat opsi lebih mudah ditemukan. + +Beberapa pengaturan khusus yang Anda pilih di masa lalu tidak ditampilkan di sini, tetapi masih aktif. + +Jika Anda melanjutkan, beberapa pengaturan Anda dapat berubah." + "Obrolan langsung" + "Pengaturan khusus per obrolan" + "Terjadi kesalahan saat memperbarui pengaturan pemberitahuan." + "Semua pesan" + "Sebutan dan Kata Kunci saja" + "Di obrolan langsung, beri tahu saya tentang" + "Di obrolan grup, beri tahu tentang" + "Aktifkan pemberitahuan di perangkat ini" + "Pengaturan belum diperbaiki, silakan coba lagi." + "Obrolan grup" + "Undangan" + "Homeserver Anda tidak mendukung opsi ini dalam ruangan terenkripsi, Anda mungkin tidak diberi tahu dalam beberapa ruangan." + "Sebutan" + "Semua" + "Sebutan" + "Beri tahu saya tentang" + "Beri tahu saya pada @room" + "Untuk menerima pemberitahuan, silakan ubah %1$s Anda." + "pengaturan sistem" + "Pemberitahuan sistem dimatikan" + "Notifikasi" + diff --git a/features/preferences/impl/src/main/res/values-ru/translations.xml b/features/preferences/impl/src/main/res/values-ru/translations.xml index 3432902dea..bbf5f426ea 100644 --- a/features/preferences/impl/src/main/res/values-ru/translations.xml +++ b/features/preferences/impl/src/main/res/values-ru/translations.xml @@ -6,6 +6,7 @@ "Режим разработчика" "Предоставьте разработчикам доступ к функциям и функциональным возможностям." "Отключить редактор форматированного текста и включить Markdown." + "Включить опцию просмотра источника сообщения на временной шкале." "Отображаемое имя" "Ваше отображаемое имя" "Произошла неизвестная ошибка, изменить информацию не удалось." @@ -28,6 +29,7 @@ "Включить уведомления на данном устройстве" "Конфигурация не была исправлена, попробуйте еще раз." "Групповые чаты" + "Приглашения" "Ваш домашний сервер не поддерживает эту опцию в зашифрованных комнатах, в некоторых комнатах вы можете не получать уведомления." "Упоминания" "Все" diff --git a/features/preferences/impl/src/main/res/values-sk/translations.xml b/features/preferences/impl/src/main/res/values-sk/translations.xml index 2b8f866255..8171748e1d 100644 --- a/features/preferences/impl/src/main/res/values-sk/translations.xml +++ b/features/preferences/impl/src/main/res/values-sk/translations.xml @@ -6,6 +6,7 @@ "Vývojársky režim" "Umožniť prístup k možnostiam a funkciám pre vývojárov." "Vypnite rozšírený textový editor na ručné písanie Markdown." + "Povoliť možnosť zobrazenia zdroja správy na časovej osi." "Zobrazované meno" "Vaše zobrazované meno" "Vyskytla sa neznáma chyba a informácie nebolo možné zmeniť." @@ -30,6 +31,7 @@ Ak budete pokračovať, niektoré z vašich nastavení sa môžu zmeniť.""Povoliť oznámenia na tomto zariadení" "Konfigurácia nebola opravená, skúste to prosím znova." "Skupinové rozhovory" + "Pozvánky" "Váš domovský server nepodporuje túto možnosť v šifrovaných miestnostiach, v niektorých miestnostiach nemusíte dostať upozornenie." "Zmienky" "Všetky" diff --git a/features/preferences/impl/src/main/res/values/localazy.xml b/features/preferences/impl/src/main/res/values/localazy.xml index 9cb6aaf698..55c43f05f4 100644 --- a/features/preferences/impl/src/main/res/values/localazy.xml +++ b/features/preferences/impl/src/main/res/values/localazy.xml @@ -6,6 +6,7 @@ "Developer mode" "Enable to have access to features and functionality for developers." "Disable the rich text editor to type Markdown manually." + "Enable option to view message source in the timeline." "Display name" "Your display name" "An unknown error was encountered and the information couldn\'t be changed." @@ -28,6 +29,7 @@ If you proceed, some of your settings may change." "Enable notifications on this device" "The configuration has not been corrected, please try again." "Group chats" + "Invitations" "Your homeserver does not support this option in encrypted rooms, you may not get notified in some rooms." "Mentions" "All" diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt index 00ea3bbf14..22ef0011a6 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt @@ -21,7 +21,7 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.libraries.featureflag.test.InMemoryPreferencesStore -import io.element.android.libraries.theme.theme.Theme +import io.element.android.compound.theme.Theme import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.awaitLastSequentialItem import kotlinx.coroutines.test.runTest diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt index fb10ee272a..07705446d9 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt @@ -67,9 +67,9 @@ class DeveloperSettingsPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val state = awaitItem() - assertThat(state.features).hasSize(FeatureFlags.entries.size) + val state = awaitLastSequentialItem() + val numberOfModifiableFeatureFlags = FeatureFlags.entries.count { it.isFinished.not() } + assertThat(state.features).hasSize(numberOfModifiableFeatureFlags) cancelAndIgnoreRemainingEvents() } } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTests.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTests.kt index f4cc7c80e9..330dea4e94 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTests.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTests.kt @@ -19,7 +19,7 @@ package io.element.android.features.preferences.impl.notifications import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingPresenter import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingStateEvents import io.element.android.libraries.matrix.api.room.RoomNotificationMode @@ -31,6 +31,7 @@ import io.element.android.libraries.matrix.test.notificationsettings.FakeNotific import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.aRoomSummaryDetail import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService +import io.element.android.tests.testutils.awaitLastSequentialItem import io.element.android.tests.testutils.consumeItemsUntilPredicate import kotlinx.coroutines.test.runTest import org.junit.Test @@ -44,13 +45,15 @@ class EditDefaultNotificationSettingsPresenterTests { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.mode).isNull() - Truth.assertThat(initialState.isOneToOne).isFalse() + assertThat(initialState.mode).isNull() + assertThat(initialState.isOneToOne).isFalse() val loadedState = consumeItemsUntilPredicate { it.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY }.last() - Truth.assertThat(loadedState.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) + assertThat(loadedState.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) + + assertThat(loadedState.displayMentionsOnlyDisclaimer).isFalse() } } @@ -73,7 +76,7 @@ class EditDefaultNotificationSettingsPresenterTests { val loadedState = consumeItemsUntilPredicate { state -> state.roomsWithUserDefinedMode.any { it.details.notificationMode == RoomNotificationMode.ALL_MESSAGES } }.last() - Truth.assertThat(loadedState.roomsWithUserDefinedMode.any { it.details.notificationMode == RoomNotificationMode.ALL_MESSAGES }).isTrue() + assertThat(loadedState.roomsWithUserDefinedMode.any { it.details.notificationMode == RoomNotificationMode.ALL_MESSAGES }).isTrue() } } @@ -87,7 +90,7 @@ class EditDefaultNotificationSettingsPresenterTests { val loadedState = consumeItemsUntilPredicate { it.mode == RoomNotificationMode.ALL_MESSAGES }.last() - Truth.assertThat(loadedState.mode).isEqualTo(RoomNotificationMode.ALL_MESSAGES) + assertThat(loadedState.mode).isEqualTo(RoomNotificationMode.ALL_MESSAGES) } } @@ -103,12 +106,25 @@ class EditDefaultNotificationSettingsPresenterTests { val errorState = consumeItemsUntilPredicate { it.changeNotificationSettingAction.isFailure() }.last() - Truth.assertThat(errorState.changeNotificationSettingAction.isFailure()).isTrue() + assertThat(errorState.changeNotificationSettingAction.isFailure()).isTrue() errorState.eventSink(EditDefaultNotificationSettingStateEvents.ClearError) val clearErrorState = consumeItemsUntilPredicate { it.changeNotificationSettingAction.isUninitialized() }.last() - Truth.assertThat(clearErrorState.changeNotificationSettingAction.isUninitialized()).isTrue() + assertThat(clearErrorState.changeNotificationSettingAction.isUninitialized()).isTrue() + } + } + + @Test + fun `present - display mentions only warning if homeserver does not support it`() = runTest { + val notificationSettingsService = FakeNotificationSettingsService().apply { + givenCanHomeServerPushEncryptedEventsToDeviceResult(Result.success(false)) + } + val presenter = createEditDefaultNotificationSettingPresenter(notificationSettingsService) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + assertThat(awaitLastSequentialItem().displayMentionsOnlyDisclaimer).isTrue() } } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt index c3d6f51256..96eca14746 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt @@ -20,7 +20,7 @@ import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStoreFactory -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.FakeMatrixClient @@ -38,20 +38,20 @@ class NotificationSettingsPresenterTests { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.appSettings.appNotificationsEnabled).isFalse() - Truth.assertThat(initialState.appSettings.systemNotificationsEnabled).isTrue() - Truth.assertThat(initialState.matrixSettings).isEqualTo(NotificationSettingsState.MatrixSettings.Uninitialized) - + assertThat(initialState.appSettings.appNotificationsEnabled).isFalse() + assertThat(initialState.appSettings.systemNotificationsEnabled).isTrue() + assertThat(initialState.matrixSettings).isEqualTo(NotificationSettingsState.MatrixSettings.Uninitialized) val loadedState = consumeItemsUntilPredicate { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid }.last() - Truth.assertThat(loadedState.appSettings.appNotificationsEnabled).isTrue() - Truth.assertThat(loadedState.appSettings.systemNotificationsEnabled).isTrue() + assertThat(loadedState.appSettings.appNotificationsEnabled).isTrue() + assertThat(loadedState.appSettings.systemNotificationsEnabled).isTrue() val valid = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid - Truth.assertThat(valid?.atRoomNotificationsEnabled).isFalse() - Truth.assertThat(valid?.callNotificationsEnabled).isFalse() - Truth.assertThat(valid?.defaultGroupNotificationMode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) - Truth.assertThat(valid?.defaultOneToOneNotificationMode).isEqualTo(RoomNotificationMode.ALL_MESSAGES) + assertThat(valid?.atRoomNotificationsEnabled).isFalse() + assertThat(valid?.callNotificationsEnabled).isFalse() + assertThat(valid?.inviteForMeNotificationsEnabled).isFalse() + assertThat(valid?.defaultGroupNotificationMode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) + assertThat(valid?.defaultOneToOneNotificationMode).isEqualTo(RoomNotificationMode.ALL_MESSAGES) cancelAndIgnoreRemainingEvents() } } @@ -63,7 +63,6 @@ class NotificationSettingsPresenterTests { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = false, mode = RoomNotificationMode.ALL_MESSAGES) notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = false, isOneToOne = false, mode = RoomNotificationMode.ALL_MESSAGES) val updatedState = consumeItemsUntilPredicate { @@ -71,7 +70,7 @@ class NotificationSettingsPresenterTests { ?.defaultGroupNotificationMode == RoomNotificationMode.ALL_MESSAGES }.last() val valid = updatedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid - Truth.assertThat(valid?.defaultGroupNotificationMode).isEqualTo(RoomNotificationMode.ALL_MESSAGES) + assertThat(valid?.defaultGroupNotificationMode).isEqualTo(RoomNotificationMode.ALL_MESSAGES) } } @@ -82,7 +81,6 @@ class NotificationSettingsPresenterTests { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - notificationSettingsService.setDefaultRoomNotificationMode( isEncrypted = true, isOneToOne = false, @@ -96,7 +94,7 @@ class NotificationSettingsPresenterTests { val updatedState = consumeItemsUntilPredicate { it.matrixSettings is NotificationSettingsState.MatrixSettings.Invalid }.last() - Truth.assertThat(updatedState.matrixSettings).isEqualTo(NotificationSettingsState.MatrixSettings.Invalid(fixFailed = false)) + assertThat(updatedState.matrixSettings).isEqualTo(NotificationSettingsState.MatrixSettings.Invalid(fixFailed = false)) } } @@ -118,9 +116,8 @@ class NotificationSettingsPresenterTests { val fixedState = consumeItemsUntilPredicate(timeout = 2000.milliseconds) { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid }.last() - val fixedMatrixState = fixedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid - Truth.assertThat(fixedMatrixState?.defaultGroupNotificationMode).isEqualTo(RoomNotificationMode.ALL_MESSAGES) + assertThat(fixedMatrixState?.defaultGroupNotificationMode).isEqualTo(RoomNotificationMode.ALL_MESSAGES) } } @@ -133,13 +130,12 @@ class NotificationSettingsPresenterTests { val loadedState = consumeItemsUntilPredicate { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid }.last() - Truth.assertThat(loadedState.appSettings.appNotificationsEnabled).isTrue() - + assertThat(loadedState.appSettings.appNotificationsEnabled).isTrue() loadedState.eventSink(NotificationSettingsEvents.SetNotificationsEnabled(false)) val updatedState = consumeItemsUntilPredicate { !it.appSettings.appNotificationsEnabled }.last() - Truth.assertThat(updatedState.appSettings.appNotificationsEnabled).isFalse() + assertThat(updatedState.appSettings.appNotificationsEnabled).isFalse() cancelAndIgnoreRemainingEvents() } } @@ -154,14 +150,34 @@ class NotificationSettingsPresenterTests { (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.callNotificationsEnabled == false }.last() val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid - Truth.assertThat(validMatrixState?.callNotificationsEnabled).isFalse() - + assertThat(validMatrixState?.callNotificationsEnabled).isFalse() loadedState.eventSink(NotificationSettingsEvents.SetCallNotificationsEnabled(true)) val updatedState = consumeItemsUntilPredicate { (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.callNotificationsEnabled == true }.last() val updatedMatrixState = updatedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid - Truth.assertThat(updatedMatrixState?.callNotificationsEnabled).isTrue() + assertThat(updatedMatrixState?.callNotificationsEnabled).isTrue() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - set invite for me notifications enabled`() = runTest { + val presenter = createNotificationSettingsPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val loadedState = consumeItemsUntilPredicate { + (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.inviteForMeNotificationsEnabled == false + }.last() + val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid + assertThat(validMatrixState?.inviteForMeNotificationsEnabled).isFalse() + loadedState.eventSink(NotificationSettingsEvents.SetInviteForMeNotificationsEnabled(true)) + val updatedState = consumeItemsUntilPredicate { + (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.inviteForMeNotificationsEnabled == true + }.last() + val updatedMatrixState = updatedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid + assertThat(updatedMatrixState?.inviteForMeNotificationsEnabled).isTrue() cancelAndIgnoreRemainingEvents() } } @@ -176,14 +192,13 @@ class NotificationSettingsPresenterTests { (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.atRoomNotificationsEnabled == false }.last() val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid - Truth.assertThat(validMatrixState?.atRoomNotificationsEnabled).isFalse() - + assertThat(validMatrixState?.atRoomNotificationsEnabled).isFalse() loadedState.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(true)) val updatedState = consumeItemsUntilPredicate { (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.atRoomNotificationsEnabled == true }.last() val updatedMatrixState = updatedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid - Truth.assertThat(updatedMatrixState?.atRoomNotificationsEnabled).isTrue() + assertThat(updatedMatrixState?.atRoomNotificationsEnabled).isTrue() cancelAndIgnoreRemainingEvents() } } @@ -200,19 +215,17 @@ class NotificationSettingsPresenterTests { (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.atRoomNotificationsEnabled == false }.last() val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid - Truth.assertThat(validMatrixState?.atRoomNotificationsEnabled).isFalse() - + assertThat(validMatrixState?.atRoomNotificationsEnabled).isFalse() loadedState.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(true)) val errorState = consumeItemsUntilPredicate { it.changeNotificationSettingAction.isFailure() }.last() - Truth.assertThat(errorState.changeNotificationSettingAction.isFailure()).isTrue() + assertThat(errorState.changeNotificationSettingAction.isFailure()).isTrue() errorState.eventSink(NotificationSettingsEvents.ClearNotificationChangeError) - val clearErrorState = consumeItemsUntilPredicate { it.changeNotificationSettingAction.isUninitialized() }.last() - Truth.assertThat(clearErrorState.changeNotificationSettingAction.isUninitialized()).isTrue() + assertThat(clearErrorState.changeNotificationSettingAction.isUninitialized()).isTrue() cancelAndIgnoreRemainingEvents() } } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt index 4adea8e59c..f914c21100 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt @@ -73,8 +73,8 @@ class PreferencesRootPresenterTest { avatarUrl = AN_AVATAR_URL ) ) - assertThat(loadedState.showDeveloperSettings).isEqualTo(true) - assertThat(loadedState.showAnalyticsSettings).isEqualTo(false) + assertThat(loadedState.showDeveloperSettings).isTrue() + assertThat(loadedState.showAnalyticsSettings).isFalse() assertThat(loadedState.accountManagementUrl).isNull() assertThat(loadedState.devicesManagementUrl).isNull() } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt index 8746670e03..3e32ed675f 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt @@ -37,6 +37,7 @@ import io.element.android.libraries.permissions.test.FakePermissionsPresenter import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.consumeItemsUntilPredicate +import io.element.android.tests.testutils.consumeItemsUntilTimeout import io.mockk.every import io.mockk.mockk import io.mockk.mockkStatic @@ -292,11 +293,10 @@ class EditUserProfilePresenterTest { val initialState = awaitItem() initialState.eventSink(EditUserProfileEvents.UpdateDisplayName(" Name ")) initialState.eventSink(EditUserProfileEvents.Save) - consumeItemsUntilPredicate { matrixClient.setDisplayNameCalled && !matrixClient.removeAvatarCalled && !matrixClient.uploadAvatarCalled } + consumeItemsUntilTimeout() assertThat(matrixClient.setDisplayNameCalled).isFalse() assertThat(matrixClient.uploadAvatarCalled).isFalse() assertThat(matrixClient.removeAvatarCalled).isFalse() - cancelAndIgnoreRemainingEvents() } } diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt index 88fe5dceb2..0672978a04 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt @@ -70,7 +70,7 @@ fun RageshakeDetectionView( @Composable private fun TakeScreenshot( - onScreenshotTaken: (ImageResult) -> Unit = {} + onScreenshotTaken: (ImageResult) -> Unit ) { val view = LocalView.current LaunchedEffect(Unit) { diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt index 99849ef1d4..d50ce28778 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt @@ -22,27 +22,19 @@ interface BugReporter { /** * Send a bug report. * - * @param reportType The report type (bug, suggestion, feedback) * @param withDevicesLogs true to include the device log * @param withCrashLogs true to include the crash logs - * @param withKeyRequestHistory true to include the crash logs * @param withScreenshot true to include the screenshot * @param theBugDescription the bug description - * @param serverVersion version of the server * @param canContact true if the user opt in to be contacted directly - * @param customFields fields which will be sent with the report * @param listener the listener */ suspend fun sendBugReport( - reportType: ReportType, withDevicesLogs: Boolean, withCrashLogs: Boolean, - withKeyRequestHistory: Boolean, withScreenshot: Boolean, theBugDescription: String, - serverVersion: String, canContact: Boolean = false, - customFields: Map? = null, listener: BugReporterListener? ) @@ -55,4 +47,9 @@ interface BugReporter { * Provide the log directory. */ fun logDirectory(): File + + /** + * Set the current tracing filter. + */ + fun setCurrentTracingFilter(tracingFilter: String) } diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporterListener.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporterListener.kt index 8f2ae90d1c..328a117ad1 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporterListener.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporterListener.kt @@ -42,5 +42,5 @@ interface BugReporterListener { /** * The bug report upload succeeded. */ - fun onUploadSucceed(reportUrl: String?) + fun onUploadSucceed() } diff --git a/features/rageshake/api/src/main/res/values-de/translations.xml b/features/rageshake/api/src/main/res/values-de/translations.xml index 6a22b4a72c..46d2be277e 100644 --- a/features/rageshake/api/src/main/res/values-de/translations.xml +++ b/features/rageshake/api/src/main/res/values-de/translations.xml @@ -1,6 +1,7 @@ "%1$s ist bei der letzten Nutzung abgestürzt. Möchtest du einen Absturzbericht mit uns teilen?" + "Du scheinst das Telefon aus Frustration zu schütteln. Möchtest du den Bildschirm für den Fehlerbericht öffnen?" "Rageshake" "Erkennungsschwelle" diff --git a/features/rageshake/api/src/main/res/values-hu/translations.xml b/features/rageshake/api/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..559594ed05 --- /dev/null +++ b/features/rageshake/api/src/main/res/values-hu/translations.xml @@ -0,0 +1,7 @@ + + + "Az %1$s összeomlott a legutóbbi használata óta. Megosztja velünk az összeomlás-jelentést?" + "Úgy tűnik, mintha mérgében a telefont rázná. Megnyitja a hibajelentési képernyőt?" + "Ideges rázás" + "Észlelési küszöb" + diff --git a/features/rageshake/api/src/main/res/values-in/translations.xml b/features/rageshake/api/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..270f62364b --- /dev/null +++ b/features/rageshake/api/src/main/res/values-in/translations.xml @@ -0,0 +1,7 @@ + + + "%1$s mengalami kemogokan saat terakhir kali digunakan. Apakah Anda ingin berbagi laporan kerusakan dengan kami?" + "Anda tampaknya mengguncang telepon karena frustrasi. Apakah Anda ingin membuka layar laporan kutu?" + "Rageshake" + "Ambang batas deteksi" + diff --git a/features/rageshake/impl/build.gradle.kts b/features/rageshake/impl/build.gradle.kts index f119cbf6c8..f87858ddc2 100644 --- a/features/rageshake/impl/build.gradle.kts +++ b/features/rageshake/impl/build.gradle.kts @@ -23,6 +23,12 @@ plugins { android { namespace = "io.element.android.features.rageshake.impl" + + testOptions { + unitTests { + isIncludeAndroidResources = true + } + } } anvil { @@ -50,12 +56,16 @@ dependencies { ksp(libs.showkase.processor) testImplementation(libs.test.junit) + testImplementation(libs.test.robolectric) testImplementation(libs.coroutines.test) testImplementation(libs.molecule.runtime) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) testImplementation(libs.test.mockk) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.libraries.sessionStorage.implMemory) testImplementation(projects.features.rageshake.test) testImplementation(projects.tests.testutils) + testImplementation(projects.services.toolbox.test) + testImplementation(libs.network.mockwebserver) } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt index 9258909201..e90787ab5c 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt @@ -25,12 +25,11 @@ import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable +import io.element.android.features.rageshake.api.crash.CrashDataStore import io.element.android.features.rageshake.api.reporter.BugReporter import io.element.android.features.rageshake.api.reporter.BugReporterListener -import io.element.android.features.rageshake.api.reporter.ReportType -import io.element.android.features.rageshake.api.crash.CrashDataStore -import io.element.android.features.rageshake.impl.logs.VectorFileLogger import io.element.android.features.rageshake.api.screenshot.ScreenshotHolder +import io.element.android.features.rageshake.impl.logs.VectorFileLogger import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter import kotlinx.coroutines.CoroutineScope @@ -64,7 +63,7 @@ class BugReportPresenter @Inject constructor( sendingAction.value = Async.Loading() } - override fun onUploadSucceed(reportUrl: String?) { + override fun onUploadSucceed() { sendingProgress.floatValue = 0f sendingAction.value = Async.Success(Unit) } @@ -135,15 +134,11 @@ class BugReportPresenter @Inject constructor( listener: BugReporterListener, ) = launch { bugReporter.sendBugReport( - reportType = ReportType.BUG_REPORT, withDevicesLogs = formState.sendLogs, withCrashLogs = hasCrashLogs && formState.sendLogs, - withKeyRequestHistory = false, withScreenshot = formState.sendScreenshot, theBugDescription = formState.description, - serverVersion = "", canContact = formState.canContact, - customFields = emptyMap(), listener = listener ) } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLogger.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLogger.kt index e848082e91..a4d06564a8 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLogger.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLogger.kt @@ -136,7 +136,7 @@ class VectorFileLogger( * * @return The list of files with logs. */ - fun getLogFiles(): List { + private fun getLogFiles(): List { return tryOrNull( onError = { Timber.e(it, "## getLogFiles() failed") } ) { diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/BugReporterUrlProvider.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/BugReporterUrlProvider.kt new file mode 100644 index 0000000000..c380ab157d --- /dev/null +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/BugReporterUrlProvider.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.rageshake.impl.reporter + +import okhttp3.HttpUrl + +fun interface BugReporterUrlProvider { + fun provide(): HttpUrl +} diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt index a693189d3c..5acf5b4103 100755 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt @@ -25,18 +25,17 @@ import com.squareup.anvil.annotations.ContributesBinding import io.element.android.features.rageshake.api.crash.CrashDataStore import io.element.android.features.rageshake.api.reporter.BugReporter import io.element.android.features.rageshake.api.reporter.BugReporterListener -import io.element.android.features.rageshake.api.reporter.ReportType import io.element.android.features.rageshake.api.screenshot.ScreenshotHolder import io.element.android.features.rageshake.impl.R import io.element.android.libraries.androidutils.file.compressFile import io.element.android.libraries.androidutils.file.safeDelete import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.data.tryOrNull -import io.element.android.libraries.core.extensions.toOnOff import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.ApplicationContext +import io.element.android.libraries.di.SingleIn import io.element.android.libraries.network.useragent.UserAgentProvider import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.services.toolbox.api.systemclock.SystemClock @@ -65,6 +64,7 @@ import javax.inject.Provider /** * BugReporter creates and sends the bug reports. */ +@SingleIn(AppScope::class) @ContributesBinding(AppScope::class) class DefaultBugReporter @Inject constructor( @ApplicationContext private val context: Context, @@ -77,86 +77,34 @@ class DefaultBugReporter @Inject constructor( private val userAgentProvider: UserAgentProvider, private val sessionStore: SessionStore, private val buildMeta: BuildMeta, - /* - private val versionProvider: VersionProvider, - private val vectorPreferences: VectorPreferences, - private val vectorFileLogger: VectorFileLogger, - private val systemLocaleProvider: SystemLocaleProvider, - private val matrix: Matrix, - private val processInfo: ProcessInfo, - private val sdkIntProvider: BuildVersionSdkIntProvider, - private val vectorLocale: VectorLocaleProvider, - */ + private val bugReporterUrlProvider: BugReporterUrlProvider, ) : BugReporter { - var inMultiWindowMode = false - companion object { // filenames - private const val LOG_CAT_ERROR_FILENAME = "logcatError.log" private const val LOG_CAT_FILENAME = "logcat.log" private const val LOG_DIRECTORY_NAME = "logs" - // private const val KEY_REQUESTS_FILENAME = "keyRequests.log" - private const val BUFFER_SIZE = 1024 * 1024 * 50 } // the pending bug report call private var bugReportCall: Call? = null - // boolean to cancel the bug report private val isCancelled = false - - /* - val adapter = MatrixJsonParser.getMoshi() - .adapter(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)) - */ - - private val logcatCommandError = arrayOf( - "logcat", // /< Run 'logcat' command - "-d", // /< Dump the log rather than continue outputting it - "-v", // formatting - "threadtime", // include timestamps - "AndroidRuntime:E " + // /< Pick all AndroidRuntime errors (such as uncaught exceptions)"communicatorjni:V " + ///< All communicatorjni logging - "libcommunicator:V " + // /< All libcommunicator logging - "DEBUG:V " + // /< All DEBUG logging - which includes native land crashes (seg faults, etc) - "*:S" // /< Everything else silent, so don't pick it.. - ) - private val logcatCommandDebug = arrayOf("logcat", "-d", "-v", "threadtime", "*:*") + private var currentTracingFilter: String? = null - /** - * Send a bug report. - * - * @param reportType The report type (bug, suggestion, feedback) - * @param withDevicesLogs true to include the device log - * @param withCrashLogs true to include the crash logs - * @param withKeyRequestHistory true to include the crash logs - * @param withScreenshot true to include the screenshot - * @param theBugDescription the bug description - * @param serverVersion version of the server - * @param canContact true if the user opt in to be contacted directly - * @param customFields fields which will be sent with the report - * @param listener the listener - */ override suspend fun sendBugReport( - reportType: ReportType, withDevicesLogs: Boolean, withCrashLogs: Boolean, - withKeyRequestHistory: Boolean, withScreenshot: Boolean, theBugDescription: String, - serverVersion: String, canContact: Boolean, - customFields: Map?, listener: BugReporterListener? ) { // enumerate files to delete val bugReportFiles: MutableList = ArrayList() - try { - var serverError: String? = null - var reportURL: String? = null withContext(coroutineDispatchers.io) { var bugDescription = theBugDescription val crashCallStack = crashDataStore.crashInfo().first() @@ -181,7 +129,7 @@ class DefaultBugReporter @Inject constructor( } if (!isCancelled && (withCrashLogs || withDevicesLogs)) { - val gzippedLogcat = saveLogCat(false) + val gzippedLogcat = saveLogCat() if (null != gzippedLogcat) { if (gzippedFiles.size == 0) { @@ -192,68 +140,24 @@ class DefaultBugReporter @Inject constructor( } } - /* - activeSessionHolder.getSafeActiveSession() - ?.takeIf { !mIsCancelled && withKeyRequestHistory } - ?.cryptoService() - ?.getGossipingEvents() - ?.let { GossipingEventsSerializer().serialize(it) } - ?.toByteArray() - ?.let { rawByteArray -> - File(context.cacheDir.absolutePath, KEY_REQUESTS_FILENAME) - .also { - it.outputStream() - .use { os -> os.write(rawByteArray) } - } - } - ?.let { compressFile(it) } - ?.let { gzippedFiles.add(it) } - */ - val sessionData = sessionStore.getLatestSession() val deviceId = sessionData?.deviceId ?: "undefined" val userId = sessionData?.userId ?: "undefined" - var olmVersion = "undefined" if (!isCancelled) { - val text = when (reportType) { - ReportType.BUG_REPORT -> bugDescription - ReportType.SUGGESTION -> "[Suggestion] $bugDescription" - ReportType.SPACE_BETA_FEEDBACK -> "[spaces-feedback] $bugDescription" - ReportType.THREADS_BETA_FEEDBACK -> "[threads-feedback] $bugDescription" - ReportType.AUTO_UISI_SENDER, - ReportType.AUTO_UISI -> bugDescription - } - // build the multi part request val builder = BugReporterMultipartBody.Builder() - .addFormDataPart("text", text) - .addFormDataPart("app", rageShakeAppNameForReport(reportType)) + .addFormDataPart("text", bugDescription) + .addFormDataPart("app", context.getString(R.string.bug_report_app_name)) .addFormDataPart("user_agent", userAgentProvider.provide()) .addFormDataPart("user_id", userId) .addFormDataPart("can_contact", canContact.toString()) .addFormDataPart("device_id", deviceId) - // .addFormDataPart("version", versionProvider.getVersion(longFormat = true)) - // .addFormDataPart("branch_name", buildMeta.gitBranchName) - // .addFormDataPart("matrix_sdk_version", Matrix.getSdkVersion()) - .addFormDataPart("olm_version", olmVersion) .addFormDataPart("device", Build.MODEL.trim()) - // .addFormDataPart("verbose_log", vectorPreferences.labAllowedExtendedLogging().toOnOff()) - .addFormDataPart("multi_window", inMultiWindowMode.toOnOff()) - // .addFormDataPart( - // "os", Build.VERSION.RELEASE + " (API " + sdkIntProvider.get() + ") " + - // Build.VERSION.INCREMENTAL + "-" + Build.VERSION.CODENAME - // ) .addFormDataPart("locale", Locale.getDefault().toString()) - // .addFormDataPart("app_language", vectorLocale.applicationLocale.toString()) - // .addFormDataPart("default_app_language", systemLocaleProvider.getSystemLocale().toString()) - // .addFormDataPart("theme", ThemeUtils.getApplicationTheme(context)) - .addFormDataPart("server_version", serverVersion) - .apply { - customFields?.forEach { (name, value) -> - addFormDataPart(name, value) - } - } + currentTracingFilter?.let { + builder.addFormDataPart("tracing_filter", it) + } // add the gzipped files, don't cancel the whole upload if only some file failed to upload var uploadedSomeLogs = false @@ -293,31 +197,6 @@ class DefaultBugReporter @Inject constructor( // add some github labels builder.addFormDataPart("label", buildMeta.versionName) - // builder.addFormDataPart("label", buildMeta.flavorDescription) - // builder.addFormDataPart("label", buildMeta.gitBranchName) - - // Possible values for BuildConfig.BUILD_TYPE: "debug", "nightly", "release". - // builder.addFormDataPart("label", BuildConfig.BUILD_TYPE) - - when (reportType) { - ReportType.BUG_REPORT -> { - /* nop */ - } - ReportType.SUGGESTION -> builder.addFormDataPart("label", "[Suggestion]") - ReportType.SPACE_BETA_FEEDBACK -> builder.addFormDataPart("label", "spaces-feedback") - ReportType.THREADS_BETA_FEEDBACK -> builder.addFormDataPart("label", "threads-feedback") - ReportType.AUTO_UISI -> { - builder.addFormDataPart("label", "Z-UISI") - builder.addFormDataPart("label", "android") - builder.addFormDataPart("label", "uisi-recipient") - } - ReportType.AUTO_UISI_SENDER -> { - builder.addFormDataPart("label", "Z-UISI") - builder.addFormDataPart("label", "android") - builder.addFormDataPart("label", "uisi-sender") - } - } - if (crashCallStack.isNotEmpty() && withCrashLogs) { builder.addFormDataPart("label", "crash") } @@ -350,7 +229,7 @@ class DefaultBugReporter @Inject constructor( // build the request val request = Request.Builder() - .url(context.getString(R.string.bug_report_url)) + .url(bugReporterUrlProvider.provide()) .post(requestBody) .build() @@ -379,7 +258,6 @@ class DefaultBugReporter @Inject constructor( } else { try { val inputStream = response.body!!.byteStream() - serverError = inputStream.use { buildString { var ch = it.read() @@ -389,7 +267,6 @@ class DefaultBugReporter @Inject constructor( } } } - // check if the error message serverError?.let { try { @@ -401,7 +278,6 @@ class DefaultBugReporter @Inject constructor( Timber.e(e, "doInBackground ; Json conversion failed") } } - // should never happen if (null == serverError) { serverError = "Failed with error $responseCode" @@ -412,25 +288,17 @@ class DefaultBugReporter @Inject constructor( Timber.e(e, "## sendBugReport() : failed to parse error") } } - } else { - /* - reportURL = response?.body?.string()?.let { stringBody -> - adapter.fromJson(stringBody)?.get("report_url")?.toString() - } - */ } } } - withContext(coroutineDispatchers.main) { bugReportCall = null - if (null != listener) { try { if (isCancelled) { listener.onUploadCancelled() } else if (null == serverError) { - listener.onUploadSucceed(reportURL) + listener.onUploadSucceed() } else { listener.onUploadFailed(serverError) } @@ -449,47 +317,6 @@ class DefaultBugReporter @Inject constructor( } } - /** - * Send a bug report either with email or with Vector. - */ - /* TODO Remove - fun openBugReportScreen(activity: FragmentActivity, reportType: ReportType = ReportType.BUG_REPORT) { - screenshot = takeScreenshot(activity) - logDbInfo() - logProcessInfo() - logOtherInfo() - activity.startActivity(BugReportActivity.intent(activity, reportType)) - } - */ - - // private fun logOtherInfo() { - // Timber.i("SyncThread state: " + activeSessionHolder.getSafeActiveSession()?.syncService()?.getSyncState()) - // } - - // private fun logDbInfo() { - // val dbInfo = matrix.debugService().getDbUsageInfo() - // Timber.i(dbInfo) - // } - - // private fun logProcessInfo() { - // val pInfo = processInfo.getInfo() - // Timber.i(pInfo) - // } - - private fun rageShakeAppNameForReport(reportType: ReportType): String { - // As per https://github.com/matrix-org/rageshake - // app: Identifier for the application (eg 'riot-web'). - // Should correspond to a mapping configured in the configuration file for github issue reporting to work. - // (see R.string.bug_report_url for configured RS server) - return context.getString( - when (reportType) { - ReportType.AUTO_UISI_SENDER, - ReportType.AUTO_UISI -> R.string.bug_report_auto_uisi_app_name - else -> R.string.bug_report_app_name - } - ) - } - override fun logDirectory(): File { return File(context.cacheDir, LOG_DIRECTORY_NAME) } @@ -501,6 +328,10 @@ class DefaultBugReporter @Inject constructor( } } + override fun setCurrentTracingFilter(tracingFilter: String) { + currentTracingFilter = tracingFilter + } + /** * @return the files on the log directory. */ @@ -545,11 +376,10 @@ class DefaultBugReporter @Inject constructor( /** * Save the logcat. * - * @param isErrorLogcat true to save the error logcat * @return the file if the operation succeeds */ - private fun saveLogCat(isErrorLogcat: Boolean): File? { - val logCatErrFile = File(context.cacheDir.absolutePath, if (isErrorLogcat) LOG_CAT_ERROR_FILENAME else LOG_CAT_FILENAME) + private fun saveLogCat(): File? { + val logCatErrFile = File(context.cacheDir.absolutePath, LOG_CAT_FILENAME) if (logCatErrFile.exists()) { logCatErrFile.safeDelete() @@ -557,7 +387,7 @@ class DefaultBugReporter @Inject constructor( try { logCatErrFile.writer().use { - getLogCatError(it, isErrorLogcat) + getLogCatError(it) } return compressFile(logCatErrFile) @@ -578,13 +408,12 @@ class DefaultBugReporter @Inject constructor( * Retrieves the logs. * * @param streamWriter the stream writer - * @param isErrorLogCat true to save the error logs */ - private fun getLogCatError(streamWriter: OutputStreamWriter, isErrorLogCat: Boolean) { + private fun getLogCatError(streamWriter: OutputStreamWriter) { val logcatProc: Process try { - logcatProc = Runtime.getRuntime().exec(if (isErrorLogCat) logcatCommandError else logcatCommandDebug) + logcatProc = Runtime.getRuntime().exec(logcatCommandDebug) } catch (e1: IOException) { return } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProvider.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProvider.kt new file mode 100644 index 0000000000..5907a9a1c9 --- /dev/null +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProvider.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.rageshake.impl.reporter + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.rageshake.impl.R +import io.element.android.libraries.di.AppScope +import io.element.android.services.toolbox.api.strings.StringProvider +import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrl +import javax.inject.Inject + +@ContributesBinding(AppScope::class) +class DefaultBugReporterUrlProvider @Inject constructor( + private val stringProvider: StringProvider +) : BugReporterUrlProvider { + override fun provide(): HttpUrl { + return stringProvider.getString(R.string.bug_report_url).toHttpUrl() + } +} diff --git a/features/rageshake/impl/src/main/res/values-de/translations.xml b/features/rageshake/impl/src/main/res/values-de/translations.xml index 68951d81f8..95e8c0228b 100644 --- a/features/rageshake/impl/src/main/res/values-de/translations.xml +++ b/features/rageshake/impl/src/main/res/values-de/translations.xml @@ -4,7 +4,7 @@ "Sie können mich kontaktieren, wenn Sie weitere Fragen haben." "Kontaktieren Sie mich" "Bildschirmfoto bearbeiten" - "Bitte beschreibe den Fehler. Was hast du getan? Was hast du erwartet, was passiert? Was ist tatsächlich passiert. Bitte gehe so detailliert wie möglich vor." + "Bitte beschreibe den Fehler. Was hast du getan? Was hast du erwartet, was passiert? Was ist tatsächlich passiert? Bitte gehe so detailliert wie möglich vor." "Beschreibe den Fehler…" "Wenn möglich, verfasse die Beschreibung bitte auf Englisch." "Absturzprotokolle senden" diff --git a/features/rageshake/impl/src/main/res/values-fr/translations.xml b/features/rageshake/impl/src/main/res/values-fr/translations.xml index ed2d9f7e96..6138f6bce5 100644 --- a/features/rageshake/impl/src/main/res/values-fr/translations.xml +++ b/features/rageshake/impl/src/main/res/values-fr/translations.xml @@ -5,7 +5,7 @@ "Contactez-moi" "Modifier la capture d’écran" "S’il vous plait, veuillez décrire le problème. Qu’avez-vous fait ? À quoi vous attendiez-vous ? Que s’est-il réellement passé ? Veuillez ajouter le plus de détails possible." - "Décrire le problème" + "Décrire le problème…" "Si possible, veuillez rédiger la description en anglais." "Envoyer des journaux d’incident" "Autoriser à inclure les journaux techniques" diff --git a/features/rageshake/impl/src/main/res/values-hu/translations.xml b/features/rageshake/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..bc09e541ab --- /dev/null +++ b/features/rageshake/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,15 @@ + + + "Képernyőkép mellékelése" + "Felveheti velem a kapcsolatot, ha bármilyen további kérdése van." + "Kapcsolat" + "Képernyőkép szerkesztése" + "Írja le a hibát. Mit csinált? Mire számított, hogy mi fog történni? Mi történt valójában? Fogalmazzon a lehető legrészletesebben." + "Írja le a problémát…" + "Ha lehetséges, a leírást angolul írja meg." + "Összeomlásnaplók küldése" + "Naplók engedélyezése" + "Képernyőkép küldése" + "A naplók szerepelni fognak az üzenetben, hogy megbizonyosodhassunk arról, hogy minden megfelelően működik-e. Ha naplók nélkül szeretné elküldeni az üzenetet, akkor kapcsolja ki ezt a beállítást." + "Az %1$s összeomlott a legutóbbi használata óta. Megosztja velünk az összeomlás-jelentést?" + diff --git a/features/rageshake/impl/src/main/res/values-in/translations.xml b/features/rageshake/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..14c1defea9 --- /dev/null +++ b/features/rageshake/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,15 @@ + + + "Lampirkan tangkapan layar" + "Anda dapat menghubungi saya jika Anda memiliki pertanyaan lebih lanjut." + "Hubungi saya" + "Sunting tangkapan layar" + "Silakan jelaskan masalah tersebut. Apa yang Anda lakukan? Apa yang Anda harapkan untuk terjadi? Apa yang sebenarnya terjadi? Jelaskan sedetail mungkin." + "Jelaskan masalah tersebut…" + "Jika memungkinkan, silakan tulis deskripsi dalam bahasa Inggris." + "Kirim log kerusakan" + "Izinkan log" + "Kirim tangkapan layar" + "Log akan disertakan dengan pesan Anda untuk memastikan bahwa semuanya berfungsi dengan baik. Untuk mengirimkan pesan Anda tanpa log, matikan pengaturan ini." + "%1$s mengalami kemogokan saat terakhir kali digunakan. Apakah Anda ingin berbagi laporan kerusakan dengan kami?" + diff --git a/features/rageshake/impl/src/main/res/values/strings.xml b/features/rageshake/impl/src/main/res/values/strings.xml index 48eff44d57..dc1069570b 100644 --- a/features/rageshake/impl/src/main/res/values/strings.xml +++ b/features/rageshake/impl/src/main/res/values/strings.xml @@ -1,5 +1,4 @@ - - https://riot.im/bugreports/submit + element-x-android - element-auto-uisi + diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt index 82edaf563d..2cd9e61398 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt @@ -18,22 +18,17 @@ package io.element.android.features.rageshake.impl.bugreport import io.element.android.features.rageshake.api.reporter.BugReporter import io.element.android.features.rageshake.api.reporter.BugReporterListener -import io.element.android.features.rageshake.api.reporter.ReportType import io.element.android.libraries.matrix.test.A_FAILURE_REASON import kotlinx.coroutines.delay import java.io.File class FakeBugReporter(val mode: FakeBugReporterMode = FakeBugReporterMode.Success) : BugReporter { override suspend fun sendBugReport( - reportType: ReportType, withDevicesLogs: Boolean, withCrashLogs: Boolean, - withKeyRequestHistory: Boolean, withScreenshot: Boolean, theBugDescription: String, - serverVersion: String, canContact: Boolean, - customFields: Map?, listener: BugReporterListener?, ) { delay(100) @@ -54,7 +49,7 @@ class FakeBugReporter(val mode: FakeBugReporterMode = FakeBugReporterMode.Succes } listener?.onProgress(100) delay(100) - listener?.onUploadSucceed(null) + listener?.onUploadSucceed() } override fun cleanLogDirectoryIfNeeded() { @@ -64,6 +59,10 @@ class FakeBugReporter(val mode: FakeBugReporterMode = FakeBugReporterMode.Succes override fun logDirectory(): File { return File("fake") } + + override fun setCurrentTracingFilter(tracingFilter: String) { + // No op + } } enum class FakeBugReporterMode { diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/VectorUncaughtExceptionHandlerTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/VectorUncaughtExceptionHandlerTest.kt new file mode 100644 index 0000000000..dc01dfa2db --- /dev/null +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/VectorUncaughtExceptionHandlerTest.kt @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.rageshake.impl.crash + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.test.AN_EXCEPTION +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +@RunWith(RobolectricTestRunner::class) +class VectorUncaughtExceptionHandlerTest { + @Test + fun `activate should change the default handler`() { + val sut = VectorUncaughtExceptionHandler(RuntimeEnvironment.getApplication()) + sut.activate() + assertThat(Thread.getDefaultUncaughtExceptionHandler()).isInstanceOf(VectorUncaughtExceptionHandler::class.java) + } + + @Test + fun `uncaught exception`() = runTest { + val crashDataStore = PreferencesCrashDataStore(RuntimeEnvironment.getApplication()) + assertThat(crashDataStore.appHasCrashed().first()).isFalse() + assertThat(crashDataStore.crashInfo().first()).isEmpty() + val sut = VectorUncaughtExceptionHandler(RuntimeEnvironment.getApplication()) + sut.uncaughtException(Thread(), AN_EXCEPTION) + assertThat(crashDataStore.appHasCrashed().first()).isTrue() + val crashInfo = crashDataStore.crashInfo().first() + assertThat(crashInfo).isNotEmpty() + assertThat(crashInfo).contains("Memory statuses") + crashDataStore.resetAppHasCrashed() + assertThat(crashDataStore.appHasCrashed().first()).isFalse() + } +} diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLoggerTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLoggerTest.kt new file mode 100644 index 0000000000..26e29e1786 --- /dev/null +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLoggerTest.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.rageshake.impl.logs + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.test.A_THROWABLE +import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +@RunWith(RobolectricTestRunner::class) +class VectorFileLoggerTest { + @Test + fun `init VectorFileLogger log debug`() = runTest { + val sut = createVectorFileLogger() + sut.d("A debug log") + } + + @Test + fun `init VectorFileLogger log error`() = runTest { + val sut = createVectorFileLogger() + sut.e(A_THROWABLE, "A debug log") + } + + @Test + fun `reset VectorFileLogger`() = runTest { + val sut = createVectorFileLogger() + sut.reset() + } + + @Test + fun `check getFromTimber`() { + assertThat(VectorFileLogger.getFromTimber()).isNull() + } + + private fun TestScope.createVectorFileLogger() = VectorFileLogger( + context = RuntimeEnvironment.getApplication(), + dispatcher = testCoroutineDispatchers().io, + ) +} diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt new file mode 100755 index 0000000000..44e085c569 --- /dev/null +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.rageshake.impl.reporter + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.rageshake.api.reporter.BugReporterListener +import io.element.android.features.rageshake.test.crash.FakeCrashDataStore +import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolder +import io.element.android.libraries.matrix.test.core.aBuildMeta +import io.element.android.libraries.network.useragent.DefaultUserAgentProvider +import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore +import io.element.android.services.toolbox.test.systemclock.FakeSystemClock +import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import okhttp3.OkHttpClient +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +@RunWith(RobolectricTestRunner::class) +class DefaultBugReporterTest { + @Test + fun `test sendBugReport success`() = runTest { + val server = MockWebServer() + server.enqueue( + MockResponse() + .setResponseCode(200) + ) + server.start() + val sut = createDefaultBugReporter(server) + var onUploadCancelledCalled = false + var onUploadFailedCalled = false + var progressValues = mutableListOf() + var onUploadSucceedCalled = false + sut.sendBugReport( + withDevicesLogs = true, + withCrashLogs = true, + withScreenshot = true, + theBugDescription = "a bug occurred", + canContact = true, + listener = object : BugReporterListener { + override fun onUploadCancelled() { + onUploadCancelledCalled = true + } + + override fun onUploadFailed(reason: String?) { + onUploadFailedCalled = true + } + + override fun onProgress(progress: Int) { + progressValues.add(progress) + } + + override fun onUploadSucceed() { + onUploadSucceedCalled = true + } + }, + ) + val request = server.takeRequest() + assertThat(request.path).isEqualTo("/") + assertThat(request.method).isEqualTo("POST") + server.shutdown() + assertThat(onUploadCancelledCalled).isFalse() + assertThat(onUploadFailedCalled).isFalse() + assertThat(progressValues.size).isEqualTo(10) + assertThat(onUploadSucceedCalled).isTrue() + } + + @Test + fun `test sendBugReport error`() = runTest { + val server = MockWebServer() + server.enqueue( + MockResponse() + .setResponseCode(400) + .setBody("""{"error": "An error body"}""") + ) + server.start() + val sut = createDefaultBugReporter(server) + var onUploadCancelledCalled = false + var onUploadFailedCalled = false + var onUploadFailedReason: String? = null + var progressValues = mutableListOf() + var onUploadSucceedCalled = false + sut.sendBugReport( + withDevicesLogs = true, + withCrashLogs = true, + withScreenshot = true, + theBugDescription = "a bug occurred", + canContact = true, + listener = object : BugReporterListener { + override fun onUploadCancelled() { + onUploadCancelledCalled = true + } + + override fun onUploadFailed(reason: String?) { + onUploadFailedCalled = true + onUploadFailedReason = reason + } + + override fun onProgress(progress: Int) { + progressValues.add(progress) + } + + override fun onUploadSucceed() { + onUploadSucceedCalled = true + } + }, + ) + val request = server.takeRequest() + assertThat(request.path).isEqualTo("/") + assertThat(request.method).isEqualTo("POST") + server.shutdown() + assertThat(onUploadCancelledCalled).isFalse() + assertThat(onUploadFailedCalled).isTrue() + assertThat(onUploadFailedReason).isEqualTo("An error body") + assertThat(progressValues.size).isEqualTo(10) + assertThat(onUploadSucceedCalled).isFalse() + } + + private fun TestScope.createDefaultBugReporter( + server: MockWebServer + ): DefaultBugReporter { + val buildMeta = aBuildMeta() + return DefaultBugReporter( + context = RuntimeEnvironment.getApplication(), + screenshotHolder = FakeScreenshotHolder(), + crashDataStore = FakeCrashDataStore(), + coroutineScope = this, + systemClock = FakeSystemClock(), + coroutineDispatchers = testCoroutineDispatchers(), + okHttpClient = { OkHttpClient.Builder().build() }, + userAgentProvider = DefaultUserAgentProvider(buildMeta), + sessionStore = InMemorySessionStore(), + buildMeta = buildMeta, + bugReporterUrlProvider = { server.url("/") } + ) + } +} diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProviderTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProviderTest.kt new file mode 100644 index 0000000000..a5c36df185 --- /dev/null +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProviderTest.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.rageshake.impl.reporter + +import com.google.common.truth.Truth.assertThat +import io.element.android.services.toolbox.test.strings.FakeStringProvider +import okhttp3.HttpUrl.Companion.toHttpUrl +import org.junit.Test + +class DefaultBugReporterUrlProviderTest { + @Test + fun `test DefaultBugReporterUrlProvider`() { + val sut = DefaultBugReporterUrlProvider(FakeStringProvider("https://example.org")) + val result = sut.provide() + assertThat(result).isEqualTo("https://example.org".toHttpUrl()) + } +} diff --git a/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt b/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt index 0aaac324da..bd1a414294 100644 --- a/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt +++ b/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt @@ -22,6 +22,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import io.element.android.libraries.architecture.FeatureEntryPoint import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import kotlinx.parcelize.Parcelize @@ -42,6 +43,7 @@ interface RoomDetailsEntryPoint : FeatureEntryPoint { interface Callback : Plugin { fun onOpenGlobalNotificationSettings() + fun onOpenRoom(roomId: RoomId) } interface NodeBuilder { diff --git a/features/roomdetails/impl/build.gradle.kts b/features/roomdetails/impl/build.gradle.kts index a2fdfa18c1..0f1a139f40 100644 --- a/features/roomdetails/impl/build.gradle.kts +++ b/features/roomdetails/impl/build.gradle.kts @@ -42,6 +42,7 @@ dependencies { implementation(projects.libraries.androidutils) implementation(projects.libraries.mediapickers.api) implementation(projects.libraries.mediaupload.api) + implementation(projects.libraries.mediaviewer.api) implementation(projects.libraries.featureflag.api) implementation(projects.libraries.permissions.api) implementation(projects.libraries.preferences.api) @@ -50,7 +51,9 @@ dependencies { api(projects.services.apperror.api) implementation(libs.coil.compose) implementation(projects.features.leaveroom.api) + implementation(projects.features.createroom.api) implementation(projects.services.analytics.api) + implementation(projects.features.poll.api) testImplementation(libs.test.junit) testImplementation(libs.coroutines.test) @@ -66,6 +69,7 @@ dependencies { testImplementation(projects.libraries.featureflag.test) testImplementation(projects.tests.testutils) testImplementation(projects.features.leaveroom.test) + testImplementation(projects.features.createroom.test) ksp(libs.showkase.processor) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index dba31ab223..76a130b681 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -19,7 +19,6 @@ package io.element.android.features.roomdetails.impl import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin @@ -29,24 +28,32 @@ import com.bumble.appyx.navmodel.backstack.operation.push import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +import io.element.android.features.poll.api.history.PollHistoryEntryPoint import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint import io.element.android.features.roomdetails.impl.edit.RoomDetailsEditNode import io.element.android.features.roomdetails.impl.invite.RoomInviteMembersNode import io.element.android.features.roomdetails.impl.members.RoomMemberListNode import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsNode +import io.element.android.features.roomdetails.impl.members.details.avatar.AvatarPreviewNode import io.element.android.features.roomdetails.impl.notificationsettings.RoomNotificationSettingsNode -import io.element.android.libraries.architecture.BackstackNode -import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler +import io.element.android.libraries.architecture.BackstackView +import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.mediaviewer.api.local.MediaInfo +import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode import kotlinx.parcelize.Parcelize @ContributesNode(RoomScope::class) class RoomDetailsFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, -) : BackstackNode( + private val pollHistoryEntryPoint: PollHistoryEntryPoint, +) : BaseFlowNode( backstack = BackStack( initialElement = plugins.filterIsInstance().first().initialElement.toNavTarget(), savedStateMap = buildContext.savedStateMap, @@ -79,6 +86,12 @@ class RoomDetailsFlowNode @AssistedInject constructor( @Parcelize data class RoomMemberDetails(val roomMemberId: UserId) : NavTarget + + @Parcelize + data class AvatarPreview(val name: String, val avatarUrl: String) : NavTarget + + @Parcelize + data object PollHistory : NavTarget } override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { @@ -100,6 +113,14 @@ class RoomDetailsFlowNode @AssistedInject constructor( override fun openRoomNotificationSettings() { backstack.push(NavTarget.RoomNotificationSettings(showUserDefinedSettingStyle = false)) } + + override fun openAvatarPreview(name: String, url: String) { + backstack.push(NavTarget.AvatarPreview(name, url)) + } + + override fun openPollHistory() { + backstack.push(NavTarget.PollHistory) + } } createNode(buildContext, listOf(roomDetailsCallback)) } @@ -136,18 +157,44 @@ class RoomDetailsFlowNode @AssistedInject constructor( } is NavTarget.RoomMemberDetails -> { - val plugins = listOf(RoomMemberDetailsNode.RoomMemberDetailsInput(navTarget.roomMemberId)) + val callback = object : RoomMemberDetailsNode.Callback { + override fun openAvatarPreview(username: String, avatarUrl: String) { + backstack.push(NavTarget.AvatarPreview(username, avatarUrl)) + } + + override fun onStartDM(roomId: RoomId) { + plugins().forEach { it.onOpenRoom(roomId) } + } + } + val plugins = listOf(RoomMemberDetailsNode.RoomMemberDetailsInput(navTarget.roomMemberId), callback) createNode(buildContext, plugins) } + is NavTarget.AvatarPreview -> { + // We need to fake the MimeType here for the viewer to work. + val mimeType = MimeTypes.Images + val input = MediaViewerNode.Inputs( + mediaInfo = MediaInfo( + name = navTarget.name, + mimeType = mimeType, + formattedFileSize = "", + fileExtension = "" + ), + mediaSource = MediaSource(url = navTarget.avatarUrl), + thumbnailSource = null, + canDownload = false, + canShare = false, + ) + createNode(buildContext, listOf(input)) + } + + is NavTarget.PollHistory -> { + pollHistoryEntryPoint.createNode(this, buildContext) + } } } @Composable override fun View(modifier: Modifier) { - Children( - navModel = backstack, - modifier = modifier, - transitionHandler = rememberDefaultTransitionHandler(), - ) + BackstackView() } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt index 65c2ed0f89..dad02d0d07 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt @@ -52,6 +52,8 @@ class RoomDetailsNode @AssistedInject constructor( fun openInviteMembers() fun editRoomDetails() fun openRoomNotificationSettings() + fun openAvatarPreview(name: String, url: String) + fun openPollHistory() } private val callbacks = plugins() @@ -76,6 +78,10 @@ class RoomDetailsNode @AssistedInject constructor( callbacks.forEach { it.openInviteMembers() } } + private fun openPollHistory() { + callbacks.forEach { it.openPollHistory() } + } + private fun onShareRoom(context: Context) { val alias = room.alias ?: room.alternativeAliases.firstOrNull() val permalinkResult = alias?.let { PermalinkBuilder.permalinkForRoomAlias(it) } @@ -110,6 +116,10 @@ class RoomDetailsNode @AssistedInject constructor( callbacks.forEach { it.editRoomDetails() } } + private fun openAvatarPreview(name: String, url: String) { + callbacks.forEach { it.openAvatarPreview(name, url) } + } + @Composable override fun View(modifier: Modifier) { val context = LocalContext.current @@ -140,6 +150,8 @@ class RoomDetailsNode @AssistedInject constructor( openRoomMemberList = ::openRoomMemberList, openRoomNotificationSettings = ::openRoomNotificationSettings, invitePeople = ::invitePeople, + openAvatarPreview = ::openAvatarPreview, + openPollHistory = ::openPollHistory, ) } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index b5b1b83b98..2879995d82 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -16,6 +16,7 @@ package io.element.android.features.roomdetails.impl +import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -45,6 +46,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.leaveroom.api.LeaveRoomView import io.element.android.features.roomdetails.impl.blockuser.BlockUserDialogs import io.element.android.features.roomdetails.impl.blockuser.BlockUserSection @@ -75,7 +78,7 @@ import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomNotificationMode -import io.element.android.libraries.theme.ElementTheme +import io.element.android.libraries.matrix.api.room.getBestName import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -88,6 +91,8 @@ fun RoomDetailsView( openRoomMemberList: () -> Unit, openRoomNotificationSettings: () -> Unit, invitePeople: () -> Unit, + openAvatarPreview: (name: String, url: String) -> Unit, + openPollHistory: () -> Unit, modifier: Modifier = Modifier, ) { fun onShareMember() { @@ -118,7 +123,10 @@ fun RoomDetailsView( avatarUrl = state.roomAvatarUrl, roomId = state.roomId, roomName = state.roomName, - roomAlias = state.roomAlias + roomAlias = state.roomAlias, + openAvatarPreview = { avatarUrl -> + openAvatarPreview(state.roomName, avatarUrl) + }, ) MainActionsSection( state = state, @@ -131,7 +139,10 @@ fun RoomDetailsView( RoomMemberHeaderSection( avatarUrl = state.roomAvatarUrl ?: member.avatarUrl, userId = member.userId.value, - userName = state.roomName + userName = state.roomName, + openAvatarPreview = { avatarUrl -> + openAvatarPreview(member.getBestName(), avatarUrl) + }, ) RoomMemberMainActionsSection(onShareUser = ::onShareMember) } @@ -165,6 +176,10 @@ fun RoomDetailsView( } } + PollsSection( + openPollHistory = openPollHistory + ) + if (state.isEncrypted) { SecuritySection() } @@ -228,7 +243,7 @@ private fun MainActionsSection(state: RoomDetailsState, onShareRoom: () -> Unit, if (roomNotificationSettings.mode == RoomNotificationMode.MUTE) { MainActionButton( title = stringResource(CommonStrings.common_unmute), - iconResourceId = CommonDrawables.ic_compound_notifications_off, + imageVector = CompoundIcons.NotificationsOff, onClick = { state.eventSink(RoomDetailsEvent.UnmuteNotification) }, @@ -236,7 +251,7 @@ private fun MainActionsSection(state: RoomDetailsState, onShareRoom: () -> Unit, } else { MainActionButton( title = stringResource(CommonStrings.common_mute), - iconResourceId = CommonDrawables.ic_compound_notifications, + imageVector = CompoundIcons.Notifications, onClick = { state.eventSink(RoomDetailsEvent.MuteNotification) }, @@ -246,7 +261,7 @@ private fun MainActionsSection(state: RoomDetailsState, onShareRoom: () -> Unit, Spacer(modifier = Modifier.width(20.dp)) MainActionButton( title = stringResource(R.string.screen_room_details_share_room_title), - iconResourceId = CommonDrawables.ic_compound_share_android, + imageVector = CompoundIcons.ShareAndroid, onClick = onShareRoom ) } @@ -258,6 +273,7 @@ private fun RoomHeaderSection( roomId: String, roomName: String, roomAlias: String?, + openAvatarPreview: (url: String) -> Unit, modifier: Modifier = Modifier ) { Column( @@ -268,7 +284,9 @@ private fun RoomHeaderSection( ) { Avatar( avatarData = AvatarData(roomId, roomName, avatarUrl, AvatarSize.RoomHeader), - modifier = Modifier.size(70.dp) + modifier = Modifier + .size(70.dp) + .clickable(enabled = avatarUrl != null) { openAvatarPreview(avatarUrl!!) } ) Spacer(modifier = Modifier.height(24.dp)) Text( @@ -330,7 +348,7 @@ private fun NotificationSection( ListItem( headlineContent = { Text(text = stringResource(R.string.screen_room_details_notification_title)) }, supportingContent = { Text(text = subtitle) }, - leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_notifications)), + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Notifications)), onClick = openRoomNotificationSettings, ) } @@ -366,6 +384,20 @@ private fun InviteSection( } } +@Composable +private fun PollsSection( + openPollHistory: () -> Unit, + modifier: Modifier = Modifier, +) { + PreferenceCategory(modifier = modifier) { + ListItem( + headlineContent = { Text(stringResource(R.string.screen_polls_history_title)) }, + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Polls)), + onClick = openPollHistory, + ) + } +} + @Composable private fun SecuritySection(modifier: Modifier = Modifier) { PreferenceCategory(title = stringResource(R.string.screen_room_details_security_title), modifier = modifier) { @@ -382,7 +414,7 @@ private fun OtherActionsSection(onLeaveRoom: () -> Unit, modifier: Modifier = Mo PreferenceCategory(showDivider = false, modifier = modifier) { ListItem( headlineContent = { Text(stringResource(R.string.screen_room_details_leave_room_title)) }, - leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_leave)), + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Leave)), style = ListItemStyle.Destructive, onClick = onLeaveRoom, ) @@ -410,5 +442,7 @@ private fun ContentToPreview(state: RoomDetailsState) { openRoomMemberList = {}, openRoomNotificationSettings = {}, invitePeople = {}, + openAvatarPreview = { _, _ -> }, + openPollHistory = {}, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserSection.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserSection.kt index 05d75211e7..b99319812b 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserSection.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserSection.kt @@ -22,6 +22,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.roomdetails.impl.R import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsEvents import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState @@ -35,7 +36,6 @@ import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.ListItem import io.element.android.libraries.designsystem.theme.components.ListItemStyle import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -82,15 +82,16 @@ private fun PreferenceBlockUser( if (isBlocked.orFalse()) { ListItem( headlineContent = { Text(stringResource(R.string.screen_dm_details_unblock_user)) }, - leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_block)), + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block)), onClick = { if (!isLoading) eventSink(RoomMemberDetailsEvents.UnblockUser(needsConfirmation = true)) }, trailingContent = if (isLoading) ListItemContent.Custom(loadingCurrentValue) else null, + style = ListItemStyle.Primary, modifier = modifier, ) } else { ListItem( headlineContent = { Text(stringResource(R.string.screen_dm_details_block_user)) }, - leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_block)), + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block)), style = ListItemStyle.Destructive, onClick = { if (!isLoading) eventSink(RoomMemberDetailsEvents.BlockUser(needsConfirmation = true)) }, trailingContent = if (isLoading) ListItemContent.Custom(loadingCurrentValue) else null, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt index ca462c6507..c65b63432e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt @@ -19,6 +19,7 @@ package io.element.android.features.roomdetails.impl.di import com.squareup.anvil.annotations.ContributesTo import dagger.Module import dagger.Provides +import io.element.android.features.createroom.api.StartDMAction import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.MatrixClient @@ -33,10 +34,11 @@ object RoomMemberModule { fun provideRoomMemberDetailsPresenterFactory( matrixClient: MatrixClient, room: MatrixRoom, + startDMAction: StartDMAction, ): RoomMemberDetailsPresenter.Factory { return object : RoomMemberDetailsPresenter.Factory { override fun create(roomMemberId: UserId): RoomMemberDetailsPresenter { - return RoomMemberDetailsPresenter(matrixClient, room, roomMemberId) + return RoomMemberDetailsPresenter(roomMemberId, matrixClient, room, startDMAction) } } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt index 90ee19c115..7fb74f628b 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt @@ -60,7 +60,7 @@ import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet import io.element.android.libraries.matrix.ui.components.EditableAvatarView import io.element.android.libraries.permissions.api.PermissionsView -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.launch diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersNode.kt index 37646aedfc..22147440e0 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersNode.kt @@ -67,7 +67,7 @@ class RoomInviteMembersNode @AssistedInject constructor( state = state, modifier = modifier, onBackPressed = { navigateUp() }, - onSendPressed = { users -> + onSubmitPressed = { users -> navigateUp() coroutineScope.launch { diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt index ef37ee26bb..6c19b45686 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt @@ -18,7 +18,6 @@ package io.element.android.features.roomdetails.impl.invite import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxWidth @@ -34,8 +33,8 @@ import androidx.compose.ui.unit.dp import io.element.android.features.roomdetails.impl.R import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.button.BackButton -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.Scaffold @@ -50,17 +49,16 @@ import io.element.android.libraries.matrix.ui.components.CheckableUserRow import io.element.android.libraries.matrix.ui.components.SelectedUsersList import io.element.android.libraries.matrix.ui.model.getAvatarData import io.element.android.libraries.matrix.ui.model.getBestName -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList -@OptIn(ExperimentalLayoutApi::class) @Composable fun RoomInviteMembersView( state: RoomInviteMembersState, + onBackPressed: () -> Unit, + onSubmitPressed: (List) -> Unit, modifier: Modifier = Modifier, - onBackPressed: () -> Unit = {}, - onSendPressed: (List) -> Unit = {}, ) { Scaffold( modifier = modifier, @@ -73,7 +71,7 @@ fun RoomInviteMembersView( onBackPressed() } }, - onSendPressed = { onSendPressed(state.selectedUsers) }, + onSubmitPressed = { onSubmitPressed(state.selectedUsers) }, canSend = state.canInvite, ) } @@ -113,9 +111,9 @@ fun RoomInviteMembersView( @Composable private fun RoomInviteMembersTopBar( canSend: Boolean, + onBackPressed: () -> Unit, + onSubmitPressed: () -> Unit, modifier: Modifier = Modifier, - onBackPressed: () -> Unit = {}, - onSendPressed: () -> Unit = {}, ) { TopAppBar( modifier = modifier, @@ -128,8 +126,8 @@ private fun RoomInviteMembersTopBar( navigationIcon = { BackButton(onClick = onBackPressed) }, actions = { TextButton( - text = stringResource(CommonStrings.action_send), - onClick = onSendPressed, + text = stringResource(CommonStrings.action_invite), + onClick = onSubmitPressed, enabled = canSend, ) } @@ -143,11 +141,11 @@ private fun RoomInviteMembersSearchBar( state: SearchBarResultState>, selectedUsers: ImmutableList, active: Boolean, + onActiveChanged: (Boolean) -> Unit, + onTextChanged: (String) -> Unit, + onUserToggled: (MatrixUser) -> Unit, modifier: Modifier = Modifier, placeHolderTitle: String = stringResource(CommonStrings.common_search_for_someone), - onActiveChanged: (Boolean) -> Unit = {}, - onTextChanged: (String) -> Unit = {}, - onUserToggled: (MatrixUser) -> Unit = {}, ) { SearchBar( query = query, @@ -219,5 +217,9 @@ private fun RoomInviteMembersSearchBar( @PreviewsDayNight @Composable internal fun RoomInviteMembersPreview(@PreviewParameter(RoomInviteMembersStateProvider::class) state: RoomInviteMembersState) = ElementPreview { - RoomInviteMembersView(state) + RoomInviteMembersView( + state = state, + onBackPressed = {}, + onSubmitPressed = {}, + ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt index f1e6447a10..4954af8efc 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt @@ -90,7 +90,7 @@ fun aRoomMember( isIgnored = isIgnored, ) -fun aRoomMemberList() = listOf( +fun aRoomMemberList() = persistentListOf( anAlice(), aBob(), aRoomMember(UserId("@carol:server.org"), "Carol"), diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt index 991ac60054..0e48a95ec1 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt @@ -56,7 +56,7 @@ import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.components.MatrixUserRow -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList @@ -177,8 +177,8 @@ private fun LazyListScope.roomMemberListSection( @Composable private fun RoomMemberListItem( roomMember: RoomMember, + onClick: () -> Unit, modifier: Modifier = Modifier, - onClick: () -> Unit = {}, ) { MatrixUserRow( modifier = modifier.clickable(onClick = onClick), @@ -195,9 +195,9 @@ private fun RoomMemberListItem( @Composable private fun RoomMemberListTopBar( canInvite: Boolean, + onBackPressed: () -> Unit, + onInvitePressed: () -> Unit, modifier: Modifier = Modifier, - onBackPressed: () -> Unit = {}, - onInvitePressed: () -> Unit = {}, ) { TopAppBar( modifier = modifier, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsEvents.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsEvents.kt index 05688c6cf7..75c66f26e2 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsEvents.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsEvents.kt @@ -17,6 +17,8 @@ package io.element.android.features.roomdetails.impl.members.details sealed interface RoomMemberDetailsEvents { + data object StartDM : RoomMemberDetailsEvents + data object ClearStartDMState : RoomMemberDetailsEvents data class BlockUser(val needsConfirmation: Boolean = false) : RoomMemberDetailsEvents data class UnblockUser(val needsConfirmation: Boolean = false) : RoomMemberDetailsEvents data object ClearBlockUserError : RoomMemberDetailsEvents diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt index 54b86f973c..3037136f63 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt @@ -17,6 +17,7 @@ package io.element.android.features.roomdetails.impl.members.details import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import com.bumble.appyx.core.lifecycle.subscribe @@ -29,9 +30,11 @@ import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.anvilannotations.ContributesNode import io.element.android.features.roomdetails.impl.R import io.element.android.libraries.androidutils.system.startSharePlainTextIntent +import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder import io.element.android.services.analytics.api.AnalyticsService @@ -46,11 +49,17 @@ class RoomMemberDetailsNode @AssistedInject constructor( presenterFactory: RoomMemberDetailsPresenter.Factory, ) : Node(buildContext, plugins = plugins) { + interface Callback : NodeInputs { + fun openAvatarPreview(username: String, avatarUrl: String) + fun onStartDM(roomId: RoomId) + } + data class RoomMemberDetailsInput( val roomMemberId: UserId ) : NodeInputs private val inputs = inputs() + private val callback = inputs() private val presenter = presenterFactory.create(inputs.roomMemberId) init { @@ -79,12 +88,24 @@ class RoomMemberDetailsNode @AssistedInject constructor( } } + fun onStartDM(roomId: RoomId) { + callback.onStartDM(roomId) + } + val state = presenter.present() + + LaunchedEffect(state.startDmActionState) { + if (state.startDmActionState is Async.Success) { + onStartDM(state.startDmActionState.data) + } + } RoomMemberDetailsView( state = state, modifier = modifier, goBack = this::navigateUp, - onShareUser = ::onShareUser + onShareUser = ::onShareUser, + onDMStarted = ::onStartDM, + openAvatarPreview = callback::openAvatarPreview, ) } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt index 3be83a2fef..e82b5f0cb5 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt @@ -27,11 +27,13 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import dagger.assisted.Assisted import dagger.assisted.AssistedInject +import io.element.android.features.createroom.api.StartDMAction import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState.ConfirmationDialog import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.ui.room.getRoomMemberAsState @@ -39,9 +41,10 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch class RoomMemberDetailsPresenter @AssistedInject constructor( + @Assisted private val roomMemberId: UserId, private val client: MatrixClient, private val room: MatrixRoom, - @Assisted private val roomMemberId: UserId, + private val startDMAction: StartDMAction, ) : Presenter { interface Factory { @@ -53,6 +56,7 @@ class RoomMemberDetailsPresenter @AssistedInject constructor( val coroutineScope = rememberCoroutineScope() var confirmationDialog by remember { mutableStateOf(null) } val roomMember by room.getRoomMemberAsState(roomMemberId) + val startDmActionState: MutableState> = remember { mutableStateOf(Async.Uninitialized) } // the room member is not really live... val isBlocked: MutableState> = remember(roomMember) { val isIgnored = roomMember?.isIgnored @@ -88,6 +92,14 @@ class RoomMemberDetailsPresenter @AssistedInject constructor( RoomMemberDetailsEvents.ClearBlockUserError -> { isBlocked.value = Async.Success(isBlocked.value.dataOrNull().orFalse()) } + RoomMemberDetailsEvents.StartDM -> { + coroutineScope.launch { + startDMAction.execute(roomMemberId, startDmActionState) + } + } + RoomMemberDetailsEvents.ClearStartDMState -> { + startDmActionState.value = Async.Uninitialized + } } } @@ -108,6 +120,7 @@ class RoomMemberDetailsPresenter @AssistedInject constructor( userName = userName, avatarUrl = userAvatar, isBlocked = isBlocked.value, + startDmActionState = startDmActionState.value, displayConfirmationDialog = confirmationDialog, isCurrentUser = client.isMe(roomMember?.userId), eventSink = ::handleEvents diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsState.kt index 957db2233e..ee9cfe388f 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsState.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsState.kt @@ -17,12 +17,14 @@ package io.element.android.features.roomdetails.impl.members.details import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.api.core.RoomId data class RoomMemberDetailsState( val userId: String, val userName: String?, val avatarUrl: String?, val isBlocked: Async, + val startDmActionState: Async, val displayConfirmationDialog: ConfirmationDialog?, val isCurrentUser: Boolean, val eventSink: (RoomMemberDetailsEvents) -> Unit diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsStateProvider.kt index b14b0e3634..c5710986a6 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsStateProvider.kt @@ -28,6 +28,7 @@ open class RoomMemberDetailsStateProvider : PreviewParameterProvider Unit, + onDMStarted: (RoomId) -> Unit, goBack: () -> Unit, + openAvatarPreview: (username: String, url: String) -> Unit, modifier: Modifier = Modifier, ) { Scaffold( @@ -62,37 +74,45 @@ fun RoomMemberDetailsView( avatarUrl = state.avatarUrl, userId = state.userId, userName = state.userName, + openAvatarPreview = { avatarUrl -> + openAvatarPreview(state.userName ?: state.userId, avatarUrl) + }, ) RoomMemberMainActionsSection(onShareUser = onShareUser) Spacer(modifier = Modifier.height(26.dp)) - // TODO implement send DM - // SendMessageSection(onSendMessage = { - // ... - // }) - if (!state.isCurrentUser) { + StartDMSection(onStartDMClicked = { state.eventSink(RoomMemberDetailsEvents.StartDM) }) BlockUserSection(state) BlockUserDialogs(state) } + AsyncView( + async = state.startDmActionState, + progressText = stringResource(CommonStrings.common_starting_chat), + onSuccess = onDMStarted, + errorMessage = { stringResource(R.string.screen_start_chat_error_starting_chat) }, + onRetry = { state.eventSink(RoomMemberDetailsEvents.StartDM) }, + onErrorDismiss = { state.eventSink(RoomMemberDetailsEvents.ClearStartDMState) }, + ) } } } -/* @Composable -private fun SendMessageSection(onSendMessage: () -> Unit, modifier: Modifier = Modifier) { - PreferenceCategory(modifier = modifier) { - PreferenceText( - title = stringResource(CommonStrings.action_send_message), - icon = Icons.Outlined.ChatBubbleOutline, - onClick = onSendMessage, - ) - } +private fun StartDMSection( + onStartDMClicked: () -> Unit, + modifier: Modifier = Modifier +) { + ListItem( + headlineContent = { Text(stringResource(CommonStrings.common_direct_chat)) }, + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Chat)), + style = ListItemStyle.Primary, + onClick = onStartDMClicked, + modifier = modifier, + ) } - */ @PreviewWithLargeHeight @Composable @@ -110,5 +130,7 @@ private fun ContentToPreview(state: RoomMemberDetailsState) { state = state, onShareUser = {}, goBack = {}, + onDMStarted = {}, + openAvatarPreview = { _, _ -> } ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberHeaderSection.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberHeaderSection.kt index 26412577b8..a5cc975b48 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberHeaderSection.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberHeaderSection.kt @@ -16,6 +16,7 @@ package io.element.android.features.roomdetails.impl.members.details +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -28,31 +29,39 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme @Composable fun RoomMemberHeaderSection( avatarUrl: String?, userId: String, userName: String?, + openAvatarPreview: (url: String) -> Unit, modifier: Modifier = Modifier ) { Column(modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Box(modifier = Modifier.size(70.dp)) { Avatar( avatarData = AvatarData(userId, userName, avatarUrl, AvatarSize.UserHeader), - modifier = Modifier.fillMaxSize() + modifier = Modifier + .clickable(enabled = avatarUrl != null) { openAvatarPreview(avatarUrl!!) } + .fillMaxSize() ) } Spacer(modifier = Modifier.height(24.dp)) if (userName != null) { - Text(text = userName, style = ElementTheme.typography.fontHeadingLgBold) + Text( + modifier = Modifier.clipToBounds(), + text = userName, + style = ElementTheme.typography.fontHeadingLgBold, + ) Spacer(modifier = Modifier.height(6.dp)) } Text( diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberMainActionsSection.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberMainActionsSection.kt index edd352f228..2ce3af5351 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberMainActionsSection.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberMainActionsSection.kt @@ -22,8 +22,8 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.button.MainActionButton -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -31,7 +31,7 @@ fun RoomMemberMainActionsSection(onShareUser: () -> Unit, modifier: Modifier = M Row(modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { MainActionButton( title = stringResource(CommonStrings.action_share), - iconResourceId = CommonDrawables.ic_compound_share_android, + imageVector = CompoundIcons.ShareAndroid, onClick = onShareUser ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/avatar/AvatarPreviewNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/avatar/AvatarPreviewNode.kt new file mode 100644 index 0000000000..ecd2806b88 --- /dev/null +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/avatar/AvatarPreviewNode.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.roomdetails.impl.members.details.avatar + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.plugin.Plugin +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import io.element.android.anvilannotations.ContributesNode +import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode +import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerPresenter + +@ContributesNode(RoomScope::class) +class AvatarPreviewNode @AssistedInject constructor( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, + presenterFactory: MediaViewerPresenter.Factory, +) : MediaViewerNode(buildContext, plugins, presenterFactory) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsItem.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsItem.kt index 182944c70e..ca78849c1c 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsItem.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsItem.kt @@ -31,7 +31,7 @@ data class RoomNotificationSettingsItem( @Composable fun roomNotificationSettingsItems(): ImmutableList { - return RoomNotificationMode.values() + return RoomNotificationMode.entries .map { when (it) { RoomNotificationMode.ALL_MESSAGES -> RoomNotificationSettingsItem( diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsOption.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsOption.kt index 11f50b763e..eaea25d202 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsOption.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsOption.kt @@ -17,78 +17,56 @@ package io.element.android.features.roomdetails.impl.notificationsettings import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.selection.selectable import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.semantics.Role -import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import androidx.compose.ui.res.stringResource +import io.element.android.features.roomdetails.impl.R +import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.theme.components.RadioButton +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.ListItem import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.toEnabledColor -import io.element.android.libraries.theme.ElementTheme +import io.element.android.libraries.matrix.api.room.RoomNotificationMode @Composable fun RoomNotificationSettingsOption( roomNotificationSettingsItem: RoomNotificationSettingsItem, + onOptionSelected: (RoomNotificationSettingsItem) -> Unit, + displayMentionsOnlyDisclaimer: Boolean, modifier: Modifier = Modifier, enabled: Boolean = true, isSelected: Boolean = false, - onOptionSelected: (RoomNotificationSettingsItem) -> Unit = {}, ) { - Row( - modifier - .fillMaxWidth() - .selectable( - selected = isSelected, - enabled = enabled, - onClick = { onOptionSelected(roomNotificationSettingsItem) }, - role = Role.RadioButton, - ) - .padding(8.dp), - ) { - Column( - Modifier - .weight(1f) - .padding(horizontal = 8.dp) - .align(Alignment.CenterVertically) - ) { - Text( - text = roomNotificationSettingsItem.title, - style = ElementTheme.typography.fontBodyLgRegular, - color = enabled.toEnabledColor(), - ) + val mode = roomNotificationSettingsItem.mode + val title = roomNotificationSettingsItem.title + val subtitle = when { + mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY && displayMentionsOnlyDisclaimer -> { + stringResource(id = R.string.screen_notification_settings_mentions_only_disclaimer) } - - RadioButton( - modifier = Modifier - .align(Alignment.CenterVertically) - .size(48.dp), - selected = isSelected, - enabled = enabled, - onClick = null // null recommended for accessibility with screenreaders - ) + else -> null } + ListItem( + modifier = modifier, + enabled = enabled, + headlineContent = { Text(title) }, + supportingContent = subtitle?.let { { Text(it) } }, + trailingContent = ListItemContent.RadioButton(selected = isSelected), + onClick = { onOptionSelected(roomNotificationSettingsItem) }, + ) } @PreviewsDayNight @Composable internal fun RoomPrivacyOptionPreview() = ElementPreview { Column { - RoomNotificationSettingsOption( - roomNotificationSettingsItem = roomNotificationSettingsItems().first(), - isSelected = true, - ) - RoomNotificationSettingsOption( - roomNotificationSettingsItem = roomNotificationSettingsItems().last(), - isSelected = false, - enabled = false, - ) + for ((index, item) in roomNotificationSettingsItems().withIndex()) { + RoomNotificationSettingsOption( + roomNotificationSettingsItem = item, + onOptionSelected = {}, + isSelected = index == 0, + enabled = index != 2, + displayMentionsOnlyDisclaimer = index == 1, + ) + } } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsOptions.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsOptions.kt index 878632db9c..37bff2ab88 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsOptions.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsOptions.kt @@ -26,8 +26,9 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationMode fun RoomNotificationSettingsOptions( selected: RoomNotificationMode?, enabled: Boolean, + onOptionSelected: (RoomNotificationSettingsItem) -> Unit, + displayMentionsOnlyDisclaimer: Boolean, modifier: Modifier = Modifier, - onOptionSelected: (RoomNotificationSettingsItem) -> Unit = {}, ) { val items = roomNotificationSettingsItems() Column(modifier = modifier.selectableGroup()) { @@ -36,6 +37,7 @@ fun RoomNotificationSettingsOptions( roomNotificationSettingsItem = item, isSelected = selected == item.mode, onOptionSelected = onOptionSelected, + displayMentionsOnlyDisclaimer = displayMentionsOnlyDisclaimer, enabled = enabled ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt index a75ea94e39..c3994e160c 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt @@ -19,10 +19,12 @@ package io.element.android.features.roomdetails.impl.notificationsettings import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -54,6 +56,7 @@ class RoomNotificationSettingsPresenter @AssistedInject constructor( @Composable override fun present(): RoomNotificationSettingsState { + var shouldDisplayMentionsOnlyDisclaimer by remember { mutableStateOf(false) } val defaultRoomNotificationMode: MutableState = rememberSaveable { mutableStateOf(null) } @@ -87,6 +90,7 @@ class RoomNotificationSettingsPresenter @AssistedInject constructor( getDefaultRoomNotificationMode(defaultRoomNotificationMode) fetchNotificationSettings(pendingRoomNotificationMode, roomNotificationSettings) observeNotificationSettings(pendingRoomNotificationMode, roomNotificationSettings) + shouldDisplayMentionsOnlyDisclaimer = room.isEncrypted && !notificationSettingsService.canHomeServerPushEncryptedEventsToDevice().getOrDefault(true) } fun handleEvents(event: RoomNotificationSettingsEvents) { @@ -124,6 +128,7 @@ class RoomNotificationSettingsPresenter @AssistedInject constructor( defaultRoomNotificationMode = defaultRoomNotificationMode.value, setNotificationSettingAction = setNotificationSettingAction.value, restoreDefaultAction = restoreDefaultAction.value, + displayMentionsOnlyDisclaimer = shouldDisplayMentionsOnlyDisclaimer, eventSink = ::handleEvents, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsState.kt index ea51636eb3..4c23c0e2ae 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsState.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsState.kt @@ -29,6 +29,7 @@ data class RoomNotificationSettingsState( val defaultRoomNotificationMode: RoomNotificationMode?, val setNotificationSettingAction: Async, val restoreDefaultAction: Async, + val displayMentionsOnlyDisclaimer: Boolean, val eventSink: (RoomNotificationSettingsEvents) -> Unit ) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsStateProvider.kt index 2df0edede7..aee68383f7 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsStateProvider.kt @@ -30,12 +30,14 @@ internal class RoomNotificationSettingsStateProvider : PreviewParameterProvider< aRoomNotificationSettingsState(setNotificationSettingAction = Async.Failure(Throwable("error"))), aRoomNotificationSettingsState(restoreDefaultAction = Async.Loading(Unit)), aRoomNotificationSettingsState(restoreDefaultAction = Async.Failure(Throwable("error"))), + aRoomNotificationSettingsState(displayMentionsOnlyDisclaimer = true) ) private fun aRoomNotificationSettingsState( isDefault: Boolean = true, setNotificationSettingAction: Async = Async.Uninitialized, restoreDefaultAction: Async = Async.Uninitialized, + displayMentionsOnlyDisclaimer: Boolean = false, ): RoomNotificationSettingsState { return RoomNotificationSettingsState( showUserDefinedSettingStyle = false, @@ -48,6 +50,7 @@ internal class RoomNotificationSettingsStateProvider : PreviewParameterProvider< defaultRoomNotificationMode = RoomNotificationMode.ALL_MESSAGES, setNotificationSettingAction = setNotificationSettingAction, restoreDefaultAction = restoreDefaultAction, + displayMentionsOnlyDisclaimer = displayMentionsOnlyDisclaimer, eventSink = { }, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt index cdb46a2fcc..6bba2436b3 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt @@ -45,15 +45,15 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.api.room.RoomNotificationMode -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @Composable fun RoomNotificationSettingsView( state: RoomNotificationSettingsState, + onShowGlobalNotifications: () -> Unit, + onBackPressed: () -> Unit, modifier: Modifier = Modifier, - onShowGlobalNotifications: () -> Unit = {}, - onBackPressed: () -> Unit = {}, ) { if (state.showUserDefinedSettingStyle) { UserDefinedRoomNotificationSettingsView( @@ -74,9 +74,9 @@ fun RoomNotificationSettingsView( @Composable private fun RoomSpecificNotificationSettingsView( state: RoomNotificationSettingsState, + onShowGlobalNotifications: () -> Unit, + onBackPressed: () -> Unit, modifier: Modifier = Modifier, - onShowGlobalNotifications: () -> Unit = {}, - onBackPressed: () -> Unit = {}, ) { Scaffold( modifier = modifier, @@ -133,10 +133,13 @@ private fun RoomSpecificNotificationSettingsView( } RoomNotificationMode.MUTE -> stringResource(id = CommonStrings.common_mute) } + val displayMentionsOnlyDisclaimer = state.displayMentionsOnlyDisclaimer + && state.defaultRoomNotificationMode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY RoomNotificationSettingsOption( roomNotificationSettingsItem = RoomNotificationSettingsItem(state.defaultRoomNotificationMode, defaultModeTitle), isSelected = true, onOptionSelected = { }, + displayMentionsOnlyDisclaimer = displayMentionsOnlyDisclaimer, enabled = true ) } @@ -146,6 +149,7 @@ private fun RoomSpecificNotificationSettingsView( RoomNotificationSettingsOptions( selected = state.displayNotificationMode, enabled = !state.displayIsDefault.orTrue(), + displayMentionsOnlyDisclaimer = state.displayMentionsOnlyDisclaimer, onOptionSelected = { state.eventSink(RoomNotificationSettingsEvents.RoomNotificationModeChanged(it.mode)) }, @@ -173,8 +177,8 @@ private fun RoomSpecificNotificationSettingsView( @OptIn(ExperimentalMaterial3Api::class) @Composable private fun RoomNotificationSettingsTopBar( + onBackPressed: () -> Unit, modifier: Modifier = Modifier, - onBackPressed: () -> Unit = {}, ) { TopAppBar( modifier = modifier, @@ -193,5 +197,9 @@ private fun RoomNotificationSettingsTopBar( internal fun RoomNotificationSettingsPreview( @PreviewParameter(RoomNotificationSettingsStateProvider::class) state: RoomNotificationSettingsState ) = ElementPreview { - RoomNotificationSettingsView(state) + RoomNotificationSettingsView( + state = state, + onShowGlobalNotifications = {}, + onBackPressed = {}, + ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsStateProvider.kt index 59181f4d9c..a3ff7f23d7 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsStateProvider.kt @@ -37,6 +37,7 @@ internal class UserDefinedRoomNotificationSettingsStateProvider : PreviewParamet defaultRoomNotificationMode = RoomNotificationMode.ALL_MESSAGES, setNotificationSettingAction = Async.Uninitialized, restoreDefaultAction = Async.Uninitialized, + displayMentionsOnlyDisclaimer = false, eventSink = { }, ), ) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt index e304ee2cfa..9b982542fd 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt @@ -42,8 +42,8 @@ import io.element.android.libraries.designsystem.theme.components.TopAppBar @Composable fun UserDefinedRoomNotificationSettingsView( state: RoomNotificationSettingsState, + onBackPressed: () -> Unit, modifier: Modifier = Modifier, - onBackPressed: () -> Unit = {}, ) { Scaffold( modifier = modifier, @@ -66,6 +66,7 @@ fun UserDefinedRoomNotificationSettingsView( RoomNotificationSettingsOptions( selected = state.displayNotificationMode, enabled = !state.displayIsDefault.orTrue(), + displayMentionsOnlyDisclaimer = state.displayMentionsOnlyDisclaimer, onOptionSelected = { state.eventSink(RoomNotificationSettingsEvents.RoomNotificationModeChanged(it.mode)) }, @@ -101,8 +102,8 @@ fun UserDefinedRoomNotificationSettingsView( @Composable private fun UserDefinedRoomNotificationSettingsTopBar( roomName: String, + onBackPressed: () -> Unit, modifier: Modifier = Modifier, - onBackPressed: () -> Unit = {}, ) { TopAppBar( modifier = modifier, @@ -120,5 +121,8 @@ private fun UserDefinedRoomNotificationSettingsTopBar( internal fun UserDefinedRoomNotificationSettingsPreview( @PreviewParameter(UserDefinedRoomNotificationSettingsStateProvider::class) state: RoomNotificationSettingsState ) = ElementPreview { - UserDefinedRoomNotificationSettingsView(state) + UserDefinedRoomNotificationSettingsView( + state = state, + onBackPressed = {}, + ) } diff --git a/features/roomdetails/impl/src/main/res/values-cs/translations.xml b/features/roomdetails/impl/src/main/res/values-cs/translations.xml index 110257d6ae..eddec59585 100644 --- a/features/roomdetails/impl/src/main/res/values-cs/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-cs/translations.xml @@ -6,6 +6,8 @@ "%1$d osob" "Při aktualizaci nastavení oznámení došlo k chybě." + "Váš domovský server tuto možnost v zašifrovaných místnostech nepodporuje, v některých místnostech nemusíte být upozorněni." + "Hlasování" "Přidat téma" "Již členem" "Již pozván(a)" @@ -36,8 +38,10 @@ "Při načítání nastavení oznámení došlo k chybě." "Obnovení výchozího režimu se nezdařilo, zkuste to prosím znovu." "Nastavení režimu se nezdařilo, zkuste to prosím znovu." + "Váš domovský server tuto možnost nepodporuje v šifrovaných místnostech, v této místnosti nebudete dostávat upozornění." "Všechny zprávy" "V této místnosti mě upozornit na" + "Při pokusu o zahájení chatu došlo k chybě" "Zablokovat" "Blokovaní uživatelé vám nebudou moci posílat zprávy a všechny jejich zprávy budou skryty. Můžete je kdykoli odblokovat." "Zablokovat uživatele" diff --git a/features/roomdetails/impl/src/main/res/values-de/translations.xml b/features/roomdetails/impl/src/main/res/values-de/translations.xml index 9589c06a1e..9a096e55ae 100644 --- a/features/roomdetails/impl/src/main/res/values-de/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-de/translations.xml @@ -5,6 +5,8 @@ "%1$d Personen" "Beim Aktualisieren der Benachrichtigungseinstellungen ist ein Fehler aufgetreten." + "Dein Homeserver unterstützt diese Option in verschlüsselten Räumen nicht. In einigen Räumen wirst du möglicherweise nicht benachrichtigt." + "Umfragen" "Thema hinzufügen" "Bereits Mitglied" "Bereits eingeladen" @@ -35,8 +37,10 @@ "Beim Laden der Benachrichtigungseinstellungen ist ein Fehler aufgetreten." "Fehler beim Wiederherstellen des Standardmodus. Bitte versuche es erneut." "Fehler beim Einstellen des Modus. Bitte versuche es erneut." + "Dein Homeserver unterstützt diese Option in verschlüsselten Räumen nicht. Du wirst in diesem Raum nicht benachrichtigt." "Alle Nachrichten" "Benachrichtige mich in diesem Raum bei" + "Beim Versuch, einen Chat zu starten, ist ein Fehler aufgetreten" "Sperren" "Gesperrte Benutzer können dir keine Nachrichten senden und alle ihre Nachrichten werden ausgeblendet. Du kannst sie jederzeit entsperren." "Benutzer sperren" diff --git a/features/roomdetails/impl/src/main/res/values-es/translations.xml b/features/roomdetails/impl/src/main/res/values-es/translations.xml index 620f974340..60353e3185 100644 --- a/features/roomdetails/impl/src/main/res/values-es/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-es/translations.xml @@ -8,6 +8,7 @@ "Cifrado de mensajes activado" "Invitar a otras personas" "Compartir sala" + "Se ha producido un error al intentar iniciar un chat" "Bloquear" "Los usuarios bloqueados no podrán enviarte mensajes y se ocultarán todos sus mensajes. Puede revertir esta acción en cualquier momento." "Bloquear usuario" diff --git a/features/roomdetails/impl/src/main/res/values-fr/translations.xml b/features/roomdetails/impl/src/main/res/values-fr/translations.xml index d2b7f22292..1611f89115 100644 --- a/features/roomdetails/impl/src/main/res/values-fr/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-fr/translations.xml @@ -5,6 +5,8 @@ "%1$d personnes" "Une erreur s’est produite lors de la mise à jour du paramètre de notification." + "Votre serveur d’accueil ne supporte pas cette option pour les salons chiffrés, vous pourriez ne pas être notifié(e) dans certains salons." + "Sondages" "Ajouter un sujet" "Déjà membre" "Déjà invité(e)" @@ -35,8 +37,10 @@ "Une erreur s’est produite lors du chargement des paramètres de notification." "Échec de la restauration du mode par défaut, veuillez réessayer." "Échec de la configuration du mode, veuillez réessayer." + "Votre serveur d’accueil ne supporte pas cette option pour les salons chiffrés, vous ne serez pas notifié(e) dans ce salon." "Tous les messages" "Dans ce salon, prévenez-moi pour" + "Une erreur s’est produite lors de la tentative de création de la discussion" "Bloquer" "Les utilisateurs bloqués ne pourront pas vous envoyer de messages et tous leurs messages seront masqués. Vous pouvez les débloquer à tout moment." "Bloquer l’utilisateur" diff --git a/features/roomdetails/impl/src/main/res/values-hu/translations.xml b/features/roomdetails/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..803b493585 --- /dev/null +++ b/features/roomdetails/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,54 @@ + + + + "%1$d személy" + "%1$d személy" + + "Hiba történt az értesítési beállítás frissítésekor." + "A Matrix-kiszolgálója nem támogatja ezt a beállítást a titkosított szobákban, előfordulhat, hogy egyes szobákban nem kap értesítést." + "Szavazások" + "Téma hozzáadása" + "Már tag" + "Már meghívták" + "Szoba szerkesztése" + "Ismeretlen hiba történt, és az információkat nem lehetett megváltoztatni." + "Nem sikerült frissíteni a szobát" + "Az üzeneteket kulcsok védik. Csak Ön és a címzett rendelkezik a feloldásukhoz szükséges egyedi kulcsokkal." + "Az üzenettitkosítás engedélyezve" + "Hiba történt az értesítési beállítások betöltésekor." + "Nem sikerült elnémítani ezt a szobát, próbálja újra." + "Nem sikerült feloldani a szoba némítását, próbálja újra." + "Emberek meghívása" + "Egyéni" + "Alapértelmezett" + "Értesítések" + "Szoba neve" + "Szoba megosztása" + "Szoba frissítése…" + "Függőben" + "Szoba tagjai" + "Egyéni beállítás engedélyezése" + "Ennek bekapcsolása felülírja az alapértelmezett beállítást" + "Értesítések kérése ebben a csevegésben ezekről:" + "Megváltoztathatja a %1$s." + "globális beállításokban" + "Alapértelmezett beállítás" + "Egyéni beállítás eltávolítása" + "Hiba történt az értesítési beállítások betöltésekor." + "Nem sikerült visszaállítani az alapértelmezett módot, próbálja újra." + "Nem sikerült a mód beállítása, próbálja újra." + "A Matrix-kiszolgálója nem támogatja ezt a beállítást a titkosított szobákban, egyes szobákban nem fog értesítéseket kapni." + "Összes üzenet" + "Ebben a szobában, értesítés ezekről:" + "Hiba történt a csevegés indításakor" + "Letiltás" + "A letiltott felhasználók nem fognak tudni üzeneteket küldeni, és az összes üzenetük rejtve lesz. Bármikor feloldhatja a letiltásukat." + "Felhasználó letiltása" + "Letiltás feloldása" + "Újra láthatja az összes üzenetét." + "Felhasználó kitiltásának feloldása" + "Szoba elhagyása" + "Biztonság" + "Téma" + "Csak említések és kulcsszavak" + diff --git a/features/roomdetails/impl/src/main/res/values-in/translations.xml b/features/roomdetails/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..d941939c41 --- /dev/null +++ b/features/roomdetails/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,52 @@ + + + + "%1$d orang" + + "Terjadi kesalahan saat memperbarui pengaturan pemberitahuan." + "Homeserver Anda tidak mendukung opsi ini dalam ruangan terenkripsi, Anda mungkin tidak diberi tahu dalam beberapa ruangan." + "Tambahkan topik" + "Sudah menjadi anggota" + "Sudah diundang" + "Sunting Ruangan" + "Terjadi kesalahan yang tidak diketahui dan informasinya tidak dapat diubah." + "Tidak dapat memperbarui ruangan" + "Pesan diamankan dengan kunci. Hanya Anda dan penerima yang memiliki kunci unik untuk membukanya." + "Enkripsi pesan diaktifkan" + "Terjadi kesalahan saat memuat pengaturan notifikasi." + "Gagal membisukan ruangan ini, silakan coba lagi." + "Gagal membunyikan ruangan ini, silakan coba lagi." + "Undang seseorang" + "Khusus" + "Bawaan" + "Pemberitahuan" + "Nama ruangan" + "Bagikan ruangan" + "Memperbarui ruangan…" + "Tertunda" + "Anggota ruangan" + "Izinkan pengaturan khusus" + "Mengaktifkan ini akan mengganti pengaturan bawaan Anda" + "Beri tahu saya di obrolan ini tentang" + "Anda dapat mengubahnya di %1$s Anda." + "pengaturan global" + "Pengaturan bawaan" + "Hapus pengaturan khusus" + "Terjadi kesalahan saat memuat pengaturan pemberitahuan." + "Gagal memulihkan mode bawaan, silakan coba lagi." + "Gagal mengatur mode, silakan coba lagi." + "Homeserver Anda tidak mendukung opsi ini dalam ruangan terenkripsi, Anda tidak akan diberi tahu dalam ruangan ini." + "Semua pesan" + "Di ruangan ini, beri tahu saya tentang" + "Terjadi kesalahan saat mencoba memulai obrolan" + "Blokir" + "Pengguna yang diblokir tidak akan dapat mengirim Anda pesan dan semua pesan mereka akan disembunyikan. Anda dapat membuka blokirnya kapan saja." + "Blokir pengguna" + "Buka blokir" + "Anda akan dapat melihat semua pesan dari mereka lagi." + "Buka blokir pengguna" + "Tinggalkan ruangan" + "Keamanan" + "Topik" + "Sebutan dan Kata Kunci saja" + diff --git a/features/roomdetails/impl/src/main/res/values-it/translations.xml b/features/roomdetails/impl/src/main/res/values-it/translations.xml index c00fb14bc1..3217efe862 100644 --- a/features/roomdetails/impl/src/main/res/values-it/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-it/translations.xml @@ -8,6 +8,7 @@ "Crittografia messaggi abilitata" "Invita persone" "Condividi stanza" + "Si è verificato un errore durante il tentativo di avviare una chat" "Blocca" "Gli utenti bloccati non saranno in grado di inviarti nuovi messaggi e tutti quelli già esistenti saranno nascosti. Potrai annullare questa azione in qualsiasi momento." "Blocca utente" diff --git a/features/roomdetails/impl/src/main/res/values-ro/translations.xml b/features/roomdetails/impl/src/main/res/values-ro/translations.xml index 1a78709241..be6217d5e9 100644 --- a/features/roomdetails/impl/src/main/res/values-ro/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ro/translations.xml @@ -37,6 +37,7 @@ "Nu s-a reușit setarea modului, vă rugăm să încercați din nou." "Toate mesajele" "În această cameră, anunțați-mă pentru" + "A apărut o eroare la încercarea începerii conversației" "Blocați" "Utilizatorii blocați nu vă vor putea trimite mesaje și toate mesajele lor vor fi ascunse. Puteți anula această acțiune oricând." "Blocați utilizatorul" diff --git a/features/roomdetails/impl/src/main/res/values-ru/translations.xml b/features/roomdetails/impl/src/main/res/values-ru/translations.xml index 7d3eae6fff..d83820dbbf 100644 --- a/features/roomdetails/impl/src/main/res/values-ru/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ru/translations.xml @@ -6,6 +6,8 @@ "%1$d пользователей" "При обновлении настроек уведомления произошла ошибка." + "Ваш домашний сервер не поддерживает эту опцию в зашифрованных комнатах, в некоторых комнатах вы можете не получать уведомления." + "Опросы" "Добавить тему" "Уже зарегистрирован" "Уже приглашены" @@ -39,6 +41,7 @@ "Ваш домашний сервер не поддерживает эту опцию в зашифрованных комнатах, вы не будете получать уведомления в этой комнате." "Все сообщения" "В этой комнате уведомить меня о" + "Произошла ошибка при попытке открытия комнаты" "Заблокировать" "Заблокированные пользователи не смогут отправлять вам сообщения, а все их сообщения будут скрыты. Вы можете разблокировать их в любое время." "Заблокировать пользователя" diff --git a/features/roomdetails/impl/src/main/res/values-sk/translations.xml b/features/roomdetails/impl/src/main/res/values-sk/translations.xml index c7e422b920..74a489c720 100644 --- a/features/roomdetails/impl/src/main/res/values-sk/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-sk/translations.xml @@ -6,6 +6,8 @@ "%1$d osôb" "Pri aktualizácii nastavenia oznámenia došlo k chybe." + "Váš domovský server nepodporuje túto možnosť v šifrovaných miestnostiach, v niektorých miestnostiach nemusíte dostať upozornenie." + "Ankety" "Pridať tému" "Už ste členom" "Už ste pozvaní" @@ -39,6 +41,7 @@ "Váš domovský server nepodporuje túto možnosť v šifrovaných miestnostiach, v tejto miestnosti nedostanete upozornenie." "Všetky správy" "V tejto miestnosti ma upozorniť na" + "Pri pokuse o spustenie konverzácie sa vyskytla chyba" "Zablokovať" "Blokovaní používatelia vám nebudú môcť posielať správy a všetky ich správy budú skryté. Môžete ich kedykoľvek odblokovať." "Zablokovať používateľa" diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml index 5363c6a78e..13219abaf8 100644 --- a/features/roomdetails/impl/src/main/res/values/localazy.xml +++ b/features/roomdetails/impl/src/main/res/values/localazy.xml @@ -5,6 +5,8 @@ "%1$d people" "An error occurred while updating the notification setting." + "Your homeserver does not support this option in encrypted rooms, you may not get notified in some rooms." + "Polls" "Add topic" "Already a member" "Already invited" @@ -38,6 +40,7 @@ "Your homeserver does not support this option in encrypted rooms, you won\'t get notified in this room." "All messages" "In this room, notify me for" + "An error occurred when trying to start a chat" "Block" "Blocked users won\'t be able to send you messages and all their messages will be hidden. You can unblock them anytime." "Block user" diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt index 5ab79d2cee..6ffba16377 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt @@ -20,6 +20,7 @@ import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.features.createroom.test.FakeStartDMAction import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomPresenter import io.element.android.features.leaveroom.fake.FakeLeaveRoomPresenter @@ -45,10 +46,12 @@ import io.element.android.libraries.matrix.test.A_USER_ID_2 import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.consumeItemsUntilPredicate import io.element.android.tests.testutils.testCoroutineDispatchers -import io.element.android.tests.testutils.WarmUpRule +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -60,16 +63,16 @@ class RoomDetailsPresenterTests { @get:Rule val warmUpRule = WarmUpRule() - private fun createRoomDetailsPresenter( + private fun TestScope.createRoomDetailsPresenter( room: MatrixRoom, leaveRoomPresenter: LeaveRoomPresenter = FakeLeaveRoomPresenter(), - dispatchers: CoroutineDispatchers, + dispatchers: CoroutineDispatchers = testCoroutineDispatchers(), notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService() ): RoomDetailsPresenter { val matrixClient = FakeMatrixClient(notificationSettingsService = notificationSettingsService) val roomMemberDetailsPresenterFactory = object : RoomMemberDetailsPresenter.Factory { override fun create(roomMemberId: UserId): RoomMemberDetailsPresenter { - return RoomMemberDetailsPresenter(matrixClient, room, roomMemberId) + return RoomMemberDetailsPresenter(roomMemberId, matrixClient, room, FakeStartDMAction()) } } val featureFlagService = FakeFeatureFlagService( @@ -89,7 +92,7 @@ class RoomDetailsPresenterTests { @Test fun `present - initial state is created from room info`() = runTest { val room = aMatrixRoom() - val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers()) + val presenter = createRoomDetailsPresenter(room) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -108,7 +111,7 @@ class RoomDetailsPresenterTests { @Test fun `present - initial state with no room name`() = runTest { val room = aMatrixRoom(name = null) - val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers()) + val presenter = createRoomDetailsPresenter(room) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -127,10 +130,10 @@ class RoomDetailsPresenterTests { isEncrypted = true, isDirect = true, ).apply { - val roomMembers = listOf(myRoomMember, otherRoomMember) + val roomMembers = persistentListOf(myRoomMember, otherRoomMember) givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers)) } - val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers()) + val presenter = createRoomDetailsPresenter(room) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -164,7 +167,7 @@ class RoomDetailsPresenterTests { val room = aMatrixRoom().apply { givenCanInviteResult(Result.success(false)) } - val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers()) + val presenter = createRoomDetailsPresenter(room) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -179,7 +182,7 @@ class RoomDetailsPresenterTests { val room = aMatrixRoom().apply { givenCanInviteResult(Result.failure(Throwable("Whoops"))) } - val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers()) + val presenter = createRoomDetailsPresenter(room) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -197,7 +200,7 @@ class RoomDetailsPresenterTests { givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.failure(Throwable("Whelp"))) givenCanInviteResult(Result.success(false)) } - val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers()) + val presenter = createRoomDetailsPresenter(room) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -218,7 +221,7 @@ class RoomDetailsPresenterTests { isEncrypted = true, isDirect = true, ).apply { - val roomMembers = listOf(myRoomMember, otherRoomMember) + val roomMembers = persistentListOf(myRoomMember, otherRoomMember) givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers)) givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true)) @@ -226,7 +229,7 @@ class RoomDetailsPresenterTests { givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(true)) givenCanInviteResult(Result.success(false)) } - val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers()) + val presenter = createRoomDetailsPresenter(room) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -241,6 +244,7 @@ class RoomDetailsPresenterTests { cancelAndIgnoreRemainingEvents() } } + @Test fun `present - initial state when in a DM with no topic`() = runTest { val myRoomMember = aRoomMember(A_SESSION_ID) @@ -250,12 +254,12 @@ class RoomDetailsPresenterTests { isDirect = true, topic = null, ).apply { - val roomMembers = listOf(myRoomMember, otherRoomMember) + val roomMembers = persistentListOf(myRoomMember, otherRoomMember) givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers)) givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true)) } - val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers()) + val presenter = createRoomDetailsPresenter(room) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -276,7 +280,7 @@ class RoomDetailsPresenterTests { givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(true)) givenCanInviteResult(Result.success(false)) } - val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers()) + val presenter = createRoomDetailsPresenter(room) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -297,7 +301,7 @@ class RoomDetailsPresenterTests { givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(false)) givenCanInviteResult(Result.success(false)) } - val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers()) + val presenter = createRoomDetailsPresenter(room) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -315,7 +319,7 @@ class RoomDetailsPresenterTests { givenCanInviteResult(Result.success(false)) } - val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers()) + val presenter = createRoomDetailsPresenter(room) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -333,7 +337,7 @@ class RoomDetailsPresenterTests { givenCanInviteResult(Result.success(false)) } - val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers()) + val presenter = createRoomDetailsPresenter(room) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -351,7 +355,11 @@ class RoomDetailsPresenterTests { fun `present - leave room event is passed on to leave room presenter`() = runTest { val leaveRoomPresenter = FakeLeaveRoomPresenter() val room = aMatrixRoom() - val presenter = createRoomDetailsPresenter(room, leaveRoomPresenter, testCoroutineDispatchers()) + val presenter = createRoomDetailsPresenter( + room = room, + leaveRoomPresenter = leaveRoomPresenter, + dispatchers = testCoroutineDispatchers() + ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -368,14 +376,18 @@ class RoomDetailsPresenterTests { val leaveRoomPresenter = FakeLeaveRoomPresenter() val notificationSettingsService = FakeNotificationSettingsService() val room = aMatrixRoom(notificationSettingsService = notificationSettingsService) - val presenter = createRoomDetailsPresenter(room, leaveRoomPresenter, testCoroutineDispatchers(), notificationSettingsService) + val presenter = createRoomDetailsPresenter( + room = room, + leaveRoomPresenter = leaveRoomPresenter, + notificationSettingsService = notificationSettingsService, + ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { notificationSettingsService.setRoomNotificationMode(room.roomId, RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) val updatedState = consumeItemsUntilPredicate { - it.roomNotificationSettings?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY + it.roomNotificationSettings?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY }.last() assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) cancelAndIgnoreRemainingEvents() @@ -384,16 +396,15 @@ class RoomDetailsPresenterTests { @Test fun `present - mute room notifications`() = runTest { - val leaveRoomPresenter = FakeLeaveRoomPresenter() val notificationSettingsService = FakeNotificationSettingsService(initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) val room = aMatrixRoom(notificationSettingsService = notificationSettingsService) - val presenter = createRoomDetailsPresenter(room, leaveRoomPresenter, testCoroutineDispatchers(), notificationSettingsService) + val presenter = createRoomDetailsPresenter(room = room, notificationSettingsService = notificationSettingsService) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { awaitItem().eventSink(RoomDetailsEvent.MuteNotification) val updatedState = consumeItemsUntilPredicate(timeout = 250.milliseconds) { - it.roomNotificationSettings?.mode == RoomNotificationMode.MUTE + it.roomNotificationSettings?.mode == RoomNotificationMode.MUTE }.last() assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.MUTE) cancelAndIgnoreRemainingEvents() @@ -402,19 +413,18 @@ class RoomDetailsPresenterTests { @Test fun `present - unmute room notifications`() = runTest { - val leaveRoomPresenter = FakeLeaveRoomPresenter() val notificationSettingsService = FakeNotificationSettingsService( initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY, initialEncryptedGroupDefaultMode = RoomNotificationMode.ALL_MESSAGES ) val room = aMatrixRoom(notificationSettingsService = notificationSettingsService) - val presenter = createRoomDetailsPresenter(room, leaveRoomPresenter, testCoroutineDispatchers(), notificationSettingsService) + val presenter = createRoomDetailsPresenter(room = room, notificationSettingsService = notificationSettingsService) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { awaitItem().eventSink(RoomDetailsEvent.UnmuteNotification) val updatedState = consumeItemsUntilPredicate { - it.roomNotificationSettings?.mode == RoomNotificationMode.ALL_MESSAGES + it.roomNotificationSettings?.mode == RoomNotificationMode.ALL_MESSAGES }.last() assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.ALL_MESSAGES) cancelAndIgnoreRemainingEvents() diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt index eb89c40a24..2c1b399da8 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt @@ -107,7 +107,7 @@ class RoomDetailsEditPresenterTest { AvatarAction.TakePhoto, AvatarAction.Remove ) - assertThat(initialState.saveButtonEnabled).isEqualTo(false) + assertThat(initialState.saveButtonEnabled).isFalse() assertThat(initialState.saveAction).isInstanceOf(Async.Uninitialized::class.java) } } @@ -299,42 +299,42 @@ class RoomDetailsEditPresenterTest { presenter.present() }.test { val initialState = awaitItem() - assertThat(initialState.saveButtonEnabled).isEqualTo(false) + assertThat(initialState.saveButtonEnabled).isFalse() // Once a change is made, the save button is enabled initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("Name II")) awaitItem().apply { - assertThat(saveButtonEnabled).isEqualTo(true) + assertThat(saveButtonEnabled).isTrue() } // If it's reverted then the save disables again initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("Name")) awaitItem().apply { - assertThat(saveButtonEnabled).isEqualTo(false) + assertThat(saveButtonEnabled).isFalse() } // Make a change... initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("Another topic")) awaitItem().apply { - assertThat(saveButtonEnabled).isEqualTo(true) + assertThat(saveButtonEnabled).isTrue() } // Revert it... initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("My topic")) awaitItem().apply { - assertThat(saveButtonEnabled).isEqualTo(false) + assertThat(saveButtonEnabled).isFalse() } // Make a change... initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove)) awaitItem().apply { - assertThat(saveButtonEnabled).isEqualTo(true) + assertThat(saveButtonEnabled).isTrue() } // Revert it... initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) awaitItem().apply { - assertThat(saveButtonEnabled).isEqualTo(false) + assertThat(saveButtonEnabled).isFalse() } } } @@ -351,42 +351,42 @@ class RoomDetailsEditPresenterTest { presenter.present() }.test { val initialState = awaitItem() - assertThat(initialState.saveButtonEnabled).isEqualTo(false) + assertThat(initialState.saveButtonEnabled).isFalse() // Once a change is made, the save button is enabled initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("Name II")) awaitItem().apply { - assertThat(saveButtonEnabled).isEqualTo(true) + assertThat(saveButtonEnabled).isTrue() } // If it's reverted then the save disables again initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("fallback")) awaitItem().apply { - assertThat(saveButtonEnabled).isEqualTo(false) + assertThat(saveButtonEnabled).isFalse() } // Make a change... initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("Another topic")) awaitItem().apply { - assertThat(saveButtonEnabled).isEqualTo(true) + assertThat(saveButtonEnabled).isTrue() } // Revert it... initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("")) awaitItem().apply { - assertThat(saveButtonEnabled).isEqualTo(false) + assertThat(saveButtonEnabled).isFalse() } // Make a change... initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) awaitItem().apply { - assertThat(saveButtonEnabled).isEqualTo(true) + assertThat(saveButtonEnabled).isTrue() } // Revert it... initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove)) awaitItem().apply { - assertThat(saveButtonEnabled).isEqualTo(false) + assertThat(saveButtonEnabled).isFalse() } } } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt index 4033038b69..9763ed280b 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt @@ -39,6 +39,7 @@ import io.element.android.libraries.usersearch.test.FakeUserRepository import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -167,7 +168,7 @@ internal class RoomInviteMembersPresenterTest { matrixRoom = FakeMatrixRoom().apply { givenRoomMembersState( MatrixRoomMembersState.Ready( - listOf( + persistentListOf( aRoomMember(userId = joinedUser.userId, membership = RoomMembershipState.JOIN), aRoomMember(userId = invitedUser.userId, membership = RoomMembershipState.INVITE), ) @@ -227,7 +228,7 @@ internal class RoomInviteMembersPresenterTest { roomMemberListDataSource = createDataSource(FakeMatrixRoom().apply { givenRoomMembersState( MatrixRoomMembersState.Ready( - listOf( + persistentListOf( aRoomMember(userId = joinedUser.userId, membership = RoomMembershipState.JOIN), aRoomMember(userId = invitedUser.userId, membership = RoomMembershipState.INVITE), ) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt index c7e4c6d8b5..097919a5e1 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt @@ -19,7 +19,7 @@ package io.element.android.features.roomdetails.members import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import io.element.android.features.roomdetails.impl.members.RoomMemberListDataSource import io.element.android.features.roomdetails.impl.members.RoomMemberListEvents import io.element.android.features.roomdetails.impl.members.RoomMemberListPresenter @@ -53,14 +53,14 @@ class RoomMemberListPresenterTests { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.roomMembers).isInstanceOf(Async.Loading::class.java) - Truth.assertThat(initialState.searchQuery).isEmpty() - Truth.assertThat(initialState.searchResults).isInstanceOf(SearchBarResultState.NotSearching::class.java) - Truth.assertThat(initialState.isSearchActive).isFalse() + assertThat(initialState.roomMembers).isInstanceOf(Async.Loading::class.java) + assertThat(initialState.searchQuery).isEmpty() + assertThat(initialState.searchResults).isInstanceOf(SearchBarResultState.NotSearching::class.java) + assertThat(initialState.isSearchActive).isFalse() val loadedState = awaitItem() - Truth.assertThat(loadedState.roomMembers).isInstanceOf(Async.Success::class.java) - Truth.assertThat((loadedState.roomMembers as Async.Success).data.invited).isEqualTo(listOf(aVictor(), aWalter())) - Truth.assertThat((loadedState.roomMembers as Async.Success).data.joined).isNotEmpty() + assertThat(loadedState.roomMembers).isInstanceOf(Async.Success::class.java) + assertThat((loadedState.roomMembers as Async.Success).data.invited).isEqualTo(listOf(aVictor(), aWalter())) + assertThat((loadedState.roomMembers as Async.Success).data.joined).isNotEmpty() } } @@ -74,7 +74,7 @@ class RoomMemberListPresenterTests { val loadedState = awaitItem() loadedState.eventSink(RoomMemberListEvents.OnSearchActiveChanged(true)) val searchActiveState = awaitItem() - Truth.assertThat(searchActiveState.isSearchActive).isTrue() + assertThat(searchActiveState.isSearchActive).isTrue() } } @@ -90,9 +90,9 @@ class RoomMemberListPresenterTests { val searchActiveState = awaitItem() searchActiveState.eventSink(RoomMemberListEvents.UpdateSearchQuery("something")) val searchQueryUpdatedState = awaitItem() - Truth.assertThat(searchQueryUpdatedState.searchQuery).isEqualTo("something") + assertThat(searchQueryUpdatedState.searchQuery).isEqualTo("something") val searchSearchResultDelivered = awaitItem() - Truth.assertThat(searchSearchResultDelivered.searchResults).isInstanceOf(SearchBarResultState.NoResults::class.java) + assertThat(searchSearchResultDelivered.searchResults).isInstanceOf(SearchBarResultState.NoResults::class.java) } } @@ -108,10 +108,10 @@ class RoomMemberListPresenterTests { val searchActiveState = awaitItem() searchActiveState.eventSink(RoomMemberListEvents.UpdateSearchQuery("Alice")) val searchQueryUpdatedState = awaitItem() - Truth.assertThat(searchQueryUpdatedState.searchQuery).isEqualTo("Alice") + assertThat(searchQueryUpdatedState.searchQuery).isEqualTo("Alice") val searchSearchResultDelivered = awaitItem() - Truth.assertThat(searchSearchResultDelivered.searchResults).isInstanceOf(SearchBarResultState.Results::class.java) - Truth.assertThat((searchSearchResultDelivered.searchResults as SearchBarResultState.Results).results.joined.first().displayName) + assertThat(searchSearchResultDelivered.searchResults).isInstanceOf(SearchBarResultState.Results::class.java) + assertThat((searchSearchResultDelivered.searchResults as SearchBarResultState.Results).results.joined.first().displayName) .isEqualTo("Alice") } } @@ -128,7 +128,7 @@ class RoomMemberListPresenterTests { }.test { skipItems(1) val loadedState = awaitItem() - Truth.assertThat(loadedState.canInvite).isTrue() + assertThat(loadedState.canInvite).isTrue() } } @@ -144,7 +144,7 @@ class RoomMemberListPresenterTests { }.test { skipItems(1) val loadedState = awaitItem() - Truth.assertThat(loadedState.canInvite).isFalse() + assertThat(loadedState.canInvite).isFalse() } } @@ -160,7 +160,7 @@ class RoomMemberListPresenterTests { }.test { skipItems(1) val loadedState = awaitItem() - Truth.assertThat(loadedState.canInvite).isFalse() + assertThat(loadedState.canInvite).isFalse() } } } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt index a249e38823..ac429e262d 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt @@ -19,17 +19,25 @@ package io.element.android.features.roomdetails.members.details import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat +import io.element.android.features.createroom.api.StartDMAction +import io.element.android.features.createroom.test.FakeStartDMAction import io.element.android.features.roomdetails.aMatrixRoom import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsEvents import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState +import io.element.android.libraries.matrix.api.room.RoomMember +import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.tests.testutils.WarmUpRule +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -47,21 +55,24 @@ class RoomMemberDetailsPresenterTests { val room = aMatrixRoom().apply { givenUserDisplayNameResult(Result.success("A custom name")) givenUserAvatarUrlResult(Result.success("A custom avatar")) - givenRoomMembersState(MatrixRoomMembersState.Ready(listOf(roomMember))) + givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember))) } - val presenter = RoomMemberDetailsPresenter(FakeMatrixClient(), room, roomMember.userId) + val presenter = createRoomMemberDetailsPresenter( + room = room, + roomMember = roomMember + ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.userId).isEqualTo(roomMember.userId.value) - Truth.assertThat(initialState.userName).isEqualTo(roomMember.displayName) - Truth.assertThat(initialState.avatarUrl).isEqualTo(roomMember.avatarUrl) - Truth.assertThat(initialState.isBlocked).isEqualTo(Async.Success(roomMember.isIgnored)) + assertThat(initialState.userId).isEqualTo(roomMember.userId.value) + assertThat(initialState.userName).isEqualTo(roomMember.displayName) + assertThat(initialState.avatarUrl).isEqualTo(roomMember.avatarUrl) + assertThat(initialState.isBlocked).isEqualTo(Async.Success(roomMember.isIgnored)) skipItems(1) val loadedState = awaitItem() - Truth.assertThat(loadedState.userName).isEqualTo("A custom name") - Truth.assertThat(loadedState.avatarUrl).isEqualTo("A custom avatar") + assertThat(loadedState.userName).isEqualTo("A custom name") + assertThat(loadedState.avatarUrl).isEqualTo("A custom avatar") } } @@ -71,15 +82,18 @@ class RoomMemberDetailsPresenterTests { val room = aMatrixRoom().apply { givenUserDisplayNameResult(Result.failure(Throwable())) givenUserAvatarUrlResult(Result.failure(Throwable())) - givenRoomMembersState(MatrixRoomMembersState.Ready(listOf(roomMember))) + givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember))) } - val presenter = RoomMemberDetailsPresenter(FakeMatrixClient(), room, roomMember.userId) + val presenter = createRoomMemberDetailsPresenter( + room = room, + roomMember = roomMember + ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.userName).isEqualTo(roomMember.displayName) - Truth.assertThat(initialState.avatarUrl).isEqualTo(roomMember.avatarUrl) + assertThat(initialState.userName).isEqualTo(roomMember.displayName) + assertThat(initialState.avatarUrl).isEqualTo(roomMember.avatarUrl) ensureAllEventsConsumed() } @@ -91,15 +105,18 @@ class RoomMemberDetailsPresenterTests { val room = aMatrixRoom().apply { givenUserDisplayNameResult(Result.success(null)) givenUserAvatarUrlResult(Result.success(null)) - givenRoomMembersState(MatrixRoomMembersState.Ready(listOf(roomMember))) + givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember))) } - val presenter = RoomMemberDetailsPresenter(FakeMatrixClient(), room, roomMember.userId) + val presenter = createRoomMemberDetailsPresenter( + room = room, + roomMember = roomMember + ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.userName).isEqualTo(roomMember.displayName) - Truth.assertThat(initialState.avatarUrl).isEqualTo(roomMember.avatarUrl) + assertThat(initialState.userName).isEqualTo(roomMember.displayName) + assertThat(initialState.avatarUrl).isEqualTo(roomMember.avatarUrl) ensureAllEventsConsumed() } @@ -107,9 +124,7 @@ class RoomMemberDetailsPresenterTests { @Test fun `present - BlockUser needing confirmation displays confirmation dialog`() = runTest { - val room = aMatrixRoom() - val roomMember = aRoomMember() - val presenter = RoomMemberDetailsPresenter(FakeMatrixClient(), room, roomMember.userId) + val presenter = createRoomMemberDetailsPresenter() moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -117,10 +132,10 @@ class RoomMemberDetailsPresenterTests { initialState.eventSink(RoomMemberDetailsEvents.BlockUser(needsConfirmation = true)) val dialogState = awaitItem() - Truth.assertThat(dialogState.displayConfirmationDialog).isEqualTo(RoomMemberDetailsState.ConfirmationDialog.Block) + assertThat(dialogState.displayConfirmationDialog).isEqualTo(RoomMemberDetailsState.ConfirmationDialog.Block) dialogState.eventSink(RoomMemberDetailsEvents.ClearConfirmationDialog) - Truth.assertThat(awaitItem().displayConfirmationDialog).isNull() + assertThat(awaitItem().displayConfirmationDialog).isNull() ensureAllEventsConsumed() } @@ -128,49 +143,43 @@ class RoomMemberDetailsPresenterTests { @Test fun `present - BlockUser and UnblockUser without confirmation change the 'blocked' state`() = runTest { - val room = aMatrixRoom() - val roomMember = aRoomMember() - val presenter = RoomMemberDetailsPresenter(FakeMatrixClient(), room, roomMember.userId) + val presenter = createRoomMemberDetailsPresenter() moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() initialState.eventSink(RoomMemberDetailsEvents.BlockUser(needsConfirmation = false)) - Truth.assertThat(awaitItem().isBlocked.isLoading()).isTrue() - Truth.assertThat(awaitItem().isBlocked.dataOrNull()).isTrue() + assertThat(awaitItem().isBlocked.isLoading()).isTrue() + assertThat(awaitItem().isBlocked.dataOrNull()).isTrue() initialState.eventSink(RoomMemberDetailsEvents.UnblockUser(needsConfirmation = false)) - Truth.assertThat(awaitItem().isBlocked.isLoading()).isTrue() - Truth.assertThat(awaitItem().isBlocked.dataOrNull()).isFalse() + assertThat(awaitItem().isBlocked.isLoading()).isTrue() + assertThat(awaitItem().isBlocked.dataOrNull()).isFalse() } } @Test fun `present - BlockUser with error`() = runTest { - val room = aMatrixRoom() - val roomMember = aRoomMember() val matrixClient = FakeMatrixClient() matrixClient.givenIgnoreUserResult(Result.failure(A_THROWABLE)) - val presenter = RoomMemberDetailsPresenter(matrixClient, room, roomMember.userId) + val presenter = createRoomMemberDetailsPresenter(client = matrixClient) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() initialState.eventSink(RoomMemberDetailsEvents.BlockUser(needsConfirmation = false)) - Truth.assertThat(awaitItem().isBlocked.isLoading()).isTrue() + assertThat(awaitItem().isBlocked.isLoading()).isTrue() val errorState = awaitItem() - Truth.assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE) + assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE) // Clear error initialState.eventSink(RoomMemberDetailsEvents.ClearBlockUserError) - Truth.assertThat(awaitItem().isBlocked).isEqualTo(Async.Success(false)) + assertThat(awaitItem().isBlocked).isEqualTo(Async.Success(false)) } } @Test fun `present - UnblockUser needing confirmation displays confirmation dialog`() = runTest { - val room = aMatrixRoom() - val roomMember = aRoomMember() - val presenter = RoomMemberDetailsPresenter(FakeMatrixClient(), room, roomMember.userId) + val presenter = createRoomMemberDetailsPresenter() moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -178,12 +187,60 @@ class RoomMemberDetailsPresenterTests { initialState.eventSink(RoomMemberDetailsEvents.UnblockUser(needsConfirmation = true)) val dialogState = awaitItem() - Truth.assertThat(dialogState.displayConfirmationDialog).isEqualTo(RoomMemberDetailsState.ConfirmationDialog.Unblock) + assertThat(dialogState.displayConfirmationDialog).isEqualTo(RoomMemberDetailsState.ConfirmationDialog.Unblock) dialogState.eventSink(RoomMemberDetailsEvents.ClearConfirmationDialog) - Truth.assertThat(awaitItem().displayConfirmationDialog).isNull() + assertThat(awaitItem().displayConfirmationDialog).isNull() ensureAllEventsConsumed() } } + + @Test + fun `present - start DM action complete scenario`() = runTest { + val startDMAction = FakeStartDMAction() + val presenter = createRoomMemberDetailsPresenter(startDMAction = startDMAction) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.startDmActionState).isInstanceOf(Async.Uninitialized::class.java) + val startDMSuccessResult = Async.Success(A_ROOM_ID) + val startDMFailureResult = Async.Failure(A_THROWABLE) + + // Failure + startDMAction.givenExecuteResult(startDMFailureResult) + initialState.eventSink(RoomMemberDetailsEvents.StartDM) + assertThat(awaitItem().startDmActionState).isInstanceOf(Async.Loading::class.java) + awaitItem().also { state -> + assertThat(state.startDmActionState).isEqualTo(startDMFailureResult) + state.eventSink(RoomMemberDetailsEvents.ClearStartDMState) + } + + // Success + startDMAction.givenExecuteResult(startDMSuccessResult) + awaitItem().also { state -> + assertThat(state.startDmActionState).isEqualTo(Async.Uninitialized) + state.eventSink(RoomMemberDetailsEvents.StartDM) + } + assertThat(awaitItem().startDmActionState).isInstanceOf(Async.Loading::class.java) + awaitItem().also { state -> + assertThat(state.startDmActionState).isEqualTo(startDMSuccessResult) + } + } + } + + private fun createRoomMemberDetailsPresenter( + client: MatrixClient = FakeMatrixClient(), + room: MatrixRoom = aMatrixRoom(), + roomMember: RoomMember = aRoomMember(), + startDMAction: StartDMAction = FakeStartDMAction() + ): RoomMemberDetailsPresenter { + return RoomMemberDetailsPresenter( + roomMemberId = roomMember.userId, + client = client, + room = room, + startDMAction = startDMAction + ) + } } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/notificationsettings/RoomNotificationSettingsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/notificationsettings/RoomNotificationSettingsPresenterTests.kt index c4134462f7..41311a4086 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/notificationsettings/RoomNotificationSettingsPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/notificationsettings/RoomNotificationSettingsPresenterTests.kt @@ -19,7 +19,7 @@ package io.element.android.features.roomdetails.notificationsettings import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import io.element.android.features.roomdetails.aMatrixRoom import io.element.android.features.roomdetails.impl.notificationsettings.RoomNotificationSettingsEvents import io.element.android.features.roomdetails.impl.notificationsettings.RoomNotificationSettingsPresenter @@ -27,6 +27,8 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService +import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.tests.testutils.awaitLastSequentialItem import io.element.android.tests.testutils.consumeItemsUntilPredicate import kotlinx.coroutines.test.runTest import org.junit.Test @@ -39,8 +41,10 @@ class RoomNotificationSettingsPresenterTests { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.roomNotificationSettings.dataOrNull()).isNull() - Truth.assertThat(initialState.defaultRoomNotificationMode).isNull() + assertThat(initialState.roomNotificationSettings.dataOrNull()).isNull() + assertThat(initialState.defaultRoomNotificationMode).isNull() + val loadedState = awaitItem() + assertThat(loadedState.displayMentionsOnlyDisclaimer).isFalse() cancelAndIgnoreRemainingEvents() } } @@ -55,7 +59,7 @@ class RoomNotificationSettingsPresenterTests { val updatedState = consumeItemsUntilPredicate { it.roomNotificationSettings.dataOrNull()?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY }.last() - Truth.assertThat(updatedState.roomNotificationSettings.dataOrNull()?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) + assertThat(updatedState.roomNotificationSettings.dataOrNull()?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) cancelAndIgnoreRemainingEvents() } } @@ -71,7 +75,7 @@ class RoomNotificationSettingsPresenterTests { val updatedState = consumeItemsUntilPredicate { it.roomNotificationSettings.dataOrNull()?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY }.last() - Truth.assertThat(updatedState.roomNotificationSettings.dataOrNull()?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) + assertThat(updatedState.roomNotificationSettings.dataOrNull()?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) } } @@ -89,16 +93,16 @@ class RoomNotificationSettingsPresenterTests { it.setNotificationSettingAction.isFailure() }.last() - Truth.assertThat(failedState.roomNotificationSettings.dataOrNull()?.isDefault).isTrue() - Truth.assertThat(failedState.pendingSetDefault).isNull() - Truth.assertThat(failedState.setNotificationSettingAction.isFailure()).isTrue() + assertThat(failedState.roomNotificationSettings.dataOrNull()?.isDefault).isTrue() + assertThat(failedState.pendingSetDefault).isNull() + assertThat(failedState.setNotificationSettingAction.isFailure()).isTrue() failedState.eventSink(RoomNotificationSettingsEvents.ClearSetNotificationError) val errorClearedState = consumeItemsUntilPredicate { it.setNotificationSettingAction.isUninitialized() }.last() - Truth.assertThat(errorClearedState.setNotificationSettingAction.isUninitialized()).isTrue() + assertThat(errorClearedState.setNotificationSettingAction.isUninitialized()).isTrue() } } @@ -114,7 +118,7 @@ class RoomNotificationSettingsPresenterTests { val defaultState = consumeItemsUntilPredicate { it.roomNotificationSettings.dataOrNull()?.isDefault == false }.last() - Truth.assertThat(defaultState.roomNotificationSettings.dataOrNull()?.isDefault).isFalse() + assertThat(defaultState.roomNotificationSettings.dataOrNull()?.isDefault).isFalse() } } @@ -130,7 +134,7 @@ class RoomNotificationSettingsPresenterTests { val defaultState = consumeItemsUntilPredicate { it.roomNotificationSettings.dataOrNull()?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY }.last() - Truth.assertThat(defaultState.roomNotificationSettings.dataOrNull()?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) + assertThat(defaultState.roomNotificationSettings.dataOrNull()?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) cancelAndIgnoreRemainingEvents() } } @@ -149,20 +153,49 @@ class RoomNotificationSettingsPresenterTests { val failedState = consumeItemsUntilPredicate { it.restoreDefaultAction.isFailure() }.last() - Truth.assertThat(failedState.restoreDefaultAction.isFailure()).isTrue() + assertThat(failedState.restoreDefaultAction.isFailure()).isTrue() failedState.eventSink(RoomNotificationSettingsEvents.ClearRestoreDefaultError) val errorClearedState = consumeItemsUntilPredicate { it.restoreDefaultAction.isUninitialized() }.last() - Truth.assertThat(errorClearedState.restoreDefaultAction.isUninitialized()).isTrue() + assertThat(errorClearedState.restoreDefaultAction.isUninitialized()).isTrue() cancelAndIgnoreRemainingEvents() } } + + @Test + fun `present - display mentions only warning for a room if homeserver does not support it and it's encrypted`() = runTest { + val notificationService = FakeNotificationSettingsService().apply { + givenCanHomeServerPushEncryptedEventsToDeviceResult(Result.success(false)) + } + val room = aMatrixRoom(notificationSettingsService = notificationService, isEncrypted = true) + val presenter = createRoomNotificationSettingsPresenter(notificationService, room) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + assertThat(awaitLastSequentialItem().displayMentionsOnlyDisclaimer).isTrue() + } + } + + @Test + fun `present - do not display mentions only warning for a room it's not encrypted`() = runTest { + val notificationService = FakeNotificationSettingsService().apply { + givenCanHomeServerPushEncryptedEventsToDeviceResult(Result.success(false)) + } + val room = aMatrixRoom(notificationSettingsService = notificationService, isEncrypted = false) + val presenter = createRoomNotificationSettingsPresenter(notificationService, room) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + assertThat(awaitLastSequentialItem().displayMentionsOnlyDisclaimer).isFalse() + } + } + private fun createRoomNotificationSettingsPresenter( - notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService() + notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(), + room: FakeMatrixRoom = aMatrixRoom(notificationSettingsService = notificationSettingsService), ): RoomNotificationSettingsPresenter{ - val room = aMatrixRoom(notificationSettingsService = notificationSettingsService) return RoomNotificationSettingsPresenter( room = room, notificationSettingsService = notificationSettingsService, diff --git a/features/roomlist/impl/build.gradle.kts b/features/roomlist/impl/build.gradle.kts index cb70d9078a..4e415fd2a2 100644 --- a/features/roomlist/impl/build.gradle.kts +++ b/features/roomlist/impl/build.gradle.kts @@ -38,6 +38,7 @@ anvil { dependencies { implementation(projects.anvilannotations) anvil(projects.anvilcodegen) + implementation(projects.appconfig) implementation(projects.libraries.core) implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/InvitesEntryPointView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/InvitesEntryPointView.kt index fda79bc8cf..d00ac783dc 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/InvitesEntryPointView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/InvitesEntryPointView.kt @@ -37,7 +37,7 @@ import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAto import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @Composable diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt index 863b7c80be..04eb6164a4 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt @@ -24,6 +24,8 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -32,9 +34,7 @@ import io.element.android.libraries.designsystem.theme.components.ListItem import io.element.android.libraries.designsystem.theme.components.ListItemStyle import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @OptIn(ExperimentalMaterial3Api::class) @@ -87,8 +87,8 @@ private fun RoomListModalBottomSheetContent( }, modifier = Modifier.clickable { onRoomSettingsClicked(contextMenu.roomId) }, leadingContent = ListItemContent.Icon( - iconSource = IconSource.Resource( - CommonDrawables.ic_compound_settings, + iconSource = IconSource.Vector( + CompoundIcons.Settings, contentDescription = stringResource(id = CommonStrings.common_settings) ) ), @@ -98,8 +98,8 @@ private fun RoomListModalBottomSheetContent( headlineContent = { Text(text = stringResource(id = CommonStrings.action_leave_room)) }, modifier = Modifier.clickable { onLeaveRoomClicked(contextMenu.roomId) }, leadingContent = ListItemContent.Icon( - iconSource = IconSource.Resource( - CommonDrawables.ic_compound_leave, + iconSource = IconSource.Vector( + CompoundIcons.Leave, contentDescription = stringResource(id = CommonStrings.action_leave_room) ) ), diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt index 2689aac427..eee2868378 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt @@ -24,6 +24,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.rememberTopAppBarState @@ -46,6 +47,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp +import io.element.android.appconfig.RoomListConfig +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.roomlist.impl.R import io.element.android.libraries.designsystem.atomic.atoms.RedIndicatorAtom import io.element.android.libraries.designsystem.components.avatar.Avatar @@ -65,14 +69,12 @@ import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.MediumTopAppBar import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.designsystem.utils.LogCompositions import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.model.getAvatarData import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings private val avatarBloomSize = 430.dp @@ -128,8 +130,6 @@ private fun DefaultRoomListTopBar( onMenuActionClicked: (RoomListMenuAction) -> Unit, modifier: Modifier = Modifier, ) { - var showMenu by remember { mutableStateOf(false) } - // We need this to manually clip the top app bar in preview mode val previewAppBarHeight = if (LocalInspectionMode.current) { 112.dp.roundToPx() @@ -150,123 +150,134 @@ private fun DefaultRoomListTopBar( val statusBarPadding = with(LocalDensity.current) { WindowInsets.statusBars.getTop(this).toDp() } Box(modifier = modifier) { - MediumTopAppBar( - modifier = Modifier - .onSizeChanged { - appBarHeight = it.height - } - .nestedScroll(scrollBehavior.nestedScrollConnection) - .avatarBloom( - avatarData = avatarData, - background = if (ElementTheme.isLightTheme) { - // Workaround to display a very subtle bloom for avatars with very soft colors - Color(0xFFF9F9F9) - } else { - ElementTheme.materialColors.background - }, - blurSize = DpSize(avatarBloomSize, avatarBloomSize), - offset = DpOffset(24.dp, 24.dp + statusBarPadding), - clipToSize = if (appBarHeight > 0) DpSize( - avatarBloomSize, - appBarHeight.toDp() - ) else DpSize.Unspecified, - bottomSoftEdgeColor = ElementTheme.materialColors.background, - bottomSoftEdgeAlpha = 1f - collapsedFraction, - alpha = if (areSearchResultsDisplayed) 0f else 1f, - ) - .statusBarsPadding(), - colors = TopAppBarDefaults.mediumTopAppBarColors( - containerColor = Color.Transparent, - scrolledContainerColor = Color.Transparent, + val collapsedTitleTextStyle = ElementTheme.typography.aliasScreenTitle + val expandedTitleTextStyle = ElementTheme.typography.fontHeadingLgBold.copy( + // Due to a limitation of MediumTopAppBar, and to avoid the text to be truncated, + // ensure that the font size will never be bigger than 28.dp. + fontSize = 28.dp.applyScaleDown().toSp() + ) + MaterialTheme( + colorScheme = ElementTheme.materialColors, + shapes = MaterialTheme.shapes, + typography = ElementTheme.materialTypography.copy( + headlineSmall = expandedTitleTextStyle, + titleLarge = collapsedTitleTextStyle ), - title = { - val fontStyle = if (scrollBehavior.state.collapsedFraction > 0.5) - ElementTheme.typography.aliasScreenTitle - else - ElementTheme.typography.fontHeadingLgBold.copy( - // Due to a limitation of MediumTopAppBar, and to avoid the text to be truncated, - // ensure that the font size will never be bigger than 28.dp. - fontSize = 28.dp.applyScaleDown().toSp() + ) { + MediumTopAppBar( + modifier = Modifier + .onSizeChanged { + appBarHeight = it.height + } + .nestedScroll(scrollBehavior.nestedScrollConnection) + .avatarBloom( + avatarData = avatarData, + background = if (ElementTheme.isLightTheme) { + // Workaround to display a very subtle bloom for avatars with very soft colors + Color(0xFFF9F9F9) + } else { + ElementTheme.materialColors.background + }, + blurSize = DpSize(avatarBloomSize, avatarBloomSize), + offset = DpOffset(24.dp, 24.dp + statusBarPadding), + clipToSize = if (appBarHeight > 0) DpSize( + avatarBloomSize, + appBarHeight.toDp() + ) else DpSize.Unspecified, + bottomSoftEdgeColor = ElementTheme.materialColors.background, + bottomSoftEdgeAlpha = 1f - collapsedFraction, + alpha = if (areSearchResultsDisplayed) 0f else 1f, ) - Text( - style = fontStyle, - text = stringResource(id = R.string.screen_roomlist_main_space_title) - ) - }, - navigationIcon = { - avatarData?.let { - IconButton( - modifier = Modifier.testTag(TestTags.homeScreenSettings), - onClick = onOpenSettings - ) { - Avatar( - avatarData = it, - contentDescription = stringResource(CommonStrings.common_settings), - ) - if (showAvatarIndicator) { - RedIndicatorAtom( - modifier = Modifier - .padding(4.5.dp) - .align(Alignment.TopEnd) + .statusBarsPadding(), + colors = TopAppBarDefaults.mediumTopAppBarColors( + containerColor = Color.Transparent, + scrolledContainerColor = Color.Transparent, + ), + title = { + Text(text = stringResource(id = R.string.screen_roomlist_main_space_title)) + }, + navigationIcon = { + avatarData?.let { + IconButton( + modifier = Modifier.testTag(TestTags.homeScreenSettings), + onClick = onOpenSettings + ) { + Avatar( + avatarData = it, + contentDescription = stringResource(CommonStrings.common_settings), ) + if (showAvatarIndicator) { + RedIndicatorAtom( + modifier = Modifier + .padding(4.5.dp) + .align(Alignment.TopEnd) + ) + } } } - } - }, - actions = { - IconButton( - onClick = onSearchClicked, - ) { - Icon( - resourceId = CommonDrawables.ic_compound_search, - contentDescription = stringResource(CommonStrings.action_search), - ) - } - IconButton( - onClick = { showMenu = !showMenu } - ) { - Icon( - resourceId = CommonDrawables.ic_compound_overflow_vertical, - contentDescription = null, - ) - } - DropdownMenu( - expanded = showMenu, - onDismissRequest = { showMenu = false } - ) { - DropdownMenuItem( - onClick = { - showMenu = false - onMenuActionClicked(RoomListMenuAction.InviteFriends) - }, - text = { Text(stringResource(id = CommonStrings.action_invite)) }, - leadingIcon = { + }, + actions = { + IconButton( + onClick = onSearchClicked, + ) { + Icon( + imageVector = CompoundIcons.Search, + contentDescription = stringResource(CommonStrings.action_search), + ) + } + if (RoomListConfig.hasDropdownMenu) { + var showMenu by remember { mutableStateOf(false) } + IconButton( + onClick = { showMenu = !showMenu } + ) { Icon( - resourceId = CommonDrawables.ic_compound_share_android, - tint = ElementTheme.materialColors.secondary, + imageVector = CompoundIcons.OverflowVertical, contentDescription = null, ) } - ) - DropdownMenuItem( - onClick = { - showMenu = false - onMenuActionClicked(RoomListMenuAction.ReportBug) - }, - text = { Text(stringResource(id = CommonStrings.common_report_a_problem)) }, - leadingIcon = { - Icon( - resourceId = CommonDrawables.ic_compound_chat_problem, - tint = ElementTheme.materialColors.secondary, - contentDescription = null, - ) + DropdownMenu( + expanded = showMenu, + onDismissRequest = { showMenu = false } + ) { + if (RoomListConfig.showInviteMenuItem) { + DropdownMenuItem( + onClick = { + showMenu = false + onMenuActionClicked(RoomListMenuAction.InviteFriends) + }, + text = { Text(stringResource(id = CommonStrings.action_invite)) }, + leadingIcon = { + Icon( + imageVector = CompoundIcons.ShareAndroid, + tint = ElementTheme.materialColors.secondary, + contentDescription = null, + ) + } + ) + } + if (RoomListConfig.showReportProblemMenuItem) { + DropdownMenuItem( + onClick = { + showMenu = false + onMenuActionClicked(RoomListMenuAction.ReportBug) + }, + text = { Text(stringResource(id = CommonStrings.common_report_a_problem)) }, + leadingIcon = { + Icon( + imageVector = CompoundIcons.ChatProblem, + tint = ElementTheme.materialColors.secondary, + contentDescription = null, + ) + } + ) + } } - ) - } - }, - scrollBehavior = scrollBehavior, - windowInsets = WindowInsets(0.dp), - ) + } + }, + scrollBehavior = scrollBehavior, + windowInsets = WindowInsets(0.dp), + ) + } HorizontalDivider( modifier = diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryPlaceholderRow.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryPlaceholderRow.kt index 633c2cf45a..ae9aed918c 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryPlaceholderRow.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryPlaceholderRow.kt @@ -36,7 +36,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.theme.placeholderBackground -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme /** * https://www.figma.com/file/0MMNu7cTOzLOlWb7ctTkv3/Element-X?node-id=6547%3A147623 diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt index a1fa410c7f..effea847d0 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt @@ -35,12 +35,12 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.roomlist.impl.model.RoomListRoomSummary import io.element.android.features.roomlist.impl.model.RoomListRoomSummaryProvider import io.element.android.libraries.core.extensions.orEmpty @@ -54,9 +54,7 @@ import io.element.android.libraries.designsystem.theme.roomListRoomMessage import io.element.android.libraries.designsystem.theme.roomListRoomMessageDate import io.element.android.libraries.designsystem.theme.roomListRoomName import io.element.android.libraries.designsystem.theme.unreadIndicator -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.room.RoomNotificationMode -import io.element.android.libraries.theme.ElementTheme internal val minHeight = 84.dp @@ -142,7 +140,7 @@ private fun RowScope.NameAndTimestampRow(room: RoomListRoomSummary) { // Timestamp Text( text = room.timestamp ?: "", - style = ElementTheme.typography.fontBodySmRegular, + style = ElementTheme.typography.fontBodySmMedium, color = if (room.hasUnread) { ElementTheme.colors.unreadIndicator } else { @@ -178,7 +176,7 @@ private fun RowScope.LastMessageAndIndicatorRow(room: RoomListRoomSummary) { if (room.hasOngoingCall) { Icon( modifier = Modifier.size(16.dp), - resourceId = CommonDrawables.ic_compound_video_call, + imageVector = CompoundIcons.VideoCallSolid, contentDescription = null, tint = ElementTheme.colors.unreadIndicator, ) @@ -199,14 +197,14 @@ private fun NotificationIcon(room: RoomListRoomSummary) { Icon( modifier = Modifier.size(16.dp), contentDescription = null, - imageVector = ImageVector.vectorResource(CommonDrawables.ic_compound_mention), + imageVector = CompoundIcons.Mention, tint = tint, ) RoomNotificationMode.MUTE -> Icon( modifier = Modifier.size(16.dp), contentDescription = null, - imageVector = ImageVector.vectorResource(CommonDrawables.ic_compound_notifications_solid_off), + imageVector = CompoundIcons.NotificationsSolidOff, tint = tint, ) } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSource.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSource.kt index e05efd3ddd..dc89d163c3 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSource.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSource.kt @@ -45,7 +45,7 @@ class DefaultInviteStateDataSource @Inject constructor( override fun inviteState(): InvitesState { val invites by client .roomListService - .invites() + .invites .summaries .collectAsState() diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt index b8af0a60cc..e337fe5ad3 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt @@ -71,7 +71,7 @@ class RoomListDataSource @Inject constructor( fun launchIn(coroutineScope: CoroutineScope) { roomListService - .allRooms() + .allRooms .summaries .onEach { roomSummaries -> replaceWith(roomSummaries) @@ -106,7 +106,7 @@ class RoomListDataSource @Inject constructor( notificationSettingsService.notificationSettingsChangeFlow .debounce(0.5.seconds) .onEach { - roomListService.rebuildRoomSummaries() + roomListService.allRooms.rebuildSummaries() } .launchIn(appScope) } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt index 3a445e6cdc..cfaf9b8321 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt @@ -23,7 +23,7 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.RoomNotificationMode @Immutable -data class RoomListRoomSummary constructor( +data class RoomListRoomSummary( val id: String, val roomId: RoomId, val name: String = "", diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchResultView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchResultView.kt index 799e94bdbd..668c3b96bd 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchResultView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchResultView.kt @@ -48,6 +48,7 @@ import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.roomlist.impl.RoomListEvents import io.element.android.features.roomlist.impl.RoomListState import io.element.android.features.roomlist.impl.aRoomListState @@ -56,14 +57,13 @@ import io.element.android.features.roomlist.impl.contentType import io.element.android.features.roomlist.impl.model.RoomListRoomSummary import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.modifiers.applyIf -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.designsystem.utils.copy import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.ui.strings.CommonStrings @@ -153,7 +153,7 @@ private fun RoomListSearchResultContent( state.eventSink(RoomListEvents.UpdateFilter("")) }) { Icon( - resourceId = CommonDrawables.ic_compound_close, + imageVector = CompoundIcons.Close, contentDescription = stringResource(CommonStrings.action_cancel) ) } diff --git a/features/roomlist/impl/src/main/res/values-de/translations.xml b/features/roomlist/impl/src/main/res/values-de/translations.xml index 3e511f67bd..d471bd3c7b 100644 --- a/features/roomlist/impl/src/main/res/values-de/translations.xml +++ b/features/roomlist/impl/src/main/res/values-de/translations.xml @@ -1,5 +1,7 @@ + "Dein Chat-Backup ist derzeit nicht synchronisiert. Du musst deinen Wiederherstellungsschlüssel bestätigen, um Zugriff auf dein Chat-Backup zu erhalten." + "Wiederherstellungsschlüssel bestätigen." "Eine neue Unterhaltung oder einen neuen Raum erstellen" "Beginne, indem du jemandem eine Nachricht sendest." "Noch keine Chats." diff --git a/features/roomlist/impl/src/main/res/values-hu/translations.xml b/features/roomlist/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..cd5dced50a --- /dev/null +++ b/features/roomlist/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,11 @@ + + + "A csevegés biztonsági mentés nincs szinkronban. Meg kell erősítenie a helyreállítási kulcsát, hogy továbbra is hozzáférjen a csevegés biztonsági mentéséhez." + "Helyreállítási kulcs megerősítése" + "Új beszélgetés vagy szoba létrehozása" + "Kezdje azzal, hogy üzenetet küld valakinek." + "Még nincsenek csevegések." + "Összes csevegés" + "Úgy tűnik, hogy új eszközt használ. Ellenőrizze egy másik eszközzel, hogy a továbbiakban elérje a titkosított üzeneteket." + "Ellenőrizze, hogy Ön az" + diff --git a/features/roomlist/impl/src/main/res/values-in/translations.xml b/features/roomlist/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..58994e4772 --- /dev/null +++ b/features/roomlist/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,11 @@ + + + "Cadangan percakapan Anda saat ini tidak tersinkron. Anda perlu mengonfirmasi kunci pemulihan Anda untuk tetap memiliki akses ke cadangan percakapan Anda." + "Konfirmasi kunci pemulihan Anda" + "Buat percakapan atau ruangan baru" + "Mulailah dengan mengirim pesan kepada seseorang." + "Belum ada obrolan." + "Semua Obrolan" + "Sepertinya Anda menggunakan perangkat baru. Verifikasi dengan perangkat lain untuk mengakses pesan terenkripsi Anda selanjutnya." + "Verifikasi bahwa ini Anda" + diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt index 2ac418e098..4573456762 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt @@ -19,7 +19,7 @@ package io.element.android.features.roomlist.impl import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomPresenter import io.element.android.features.leaveroom.fake.FakeLeaveRoomPresenter @@ -83,13 +83,13 @@ class RoomListPresenterTests { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.matrixUser).isNull() + assertThat(initialState.matrixUser).isNull() val withUserState = awaitItem() - Truth.assertThat(withUserState.matrixUser).isNotNull() - Truth.assertThat(withUserState.matrixUser!!.userId).isEqualTo(A_USER_ID) - Truth.assertThat(withUserState.matrixUser!!.displayName).isEqualTo(A_USER_NAME) - Truth.assertThat(withUserState.matrixUser!!.avatarUrl).isEqualTo(AN_AVATAR_URL) - Truth.assertThat(withUserState.showAvatarIndicator).isFalse() + assertThat(withUserState.matrixUser).isNotNull() + assertThat(withUserState.matrixUser!!.userId).isEqualTo(A_USER_ID) + assertThat(withUserState.matrixUser!!.displayName).isEqualTo(A_USER_NAME) + assertThat(withUserState.matrixUser!!.avatarUrl).isEqualTo(AN_AVATAR_URL) + assertThat(withUserState.showAvatarIndicator).isFalse() scope.cancel() } } @@ -109,12 +109,12 @@ class RoomListPresenterTests { }.test { skipItems(1) val initialState = awaitItem() - Truth.assertThat(initialState.showAvatarIndicator).isFalse() + assertThat(initialState.showAvatarIndicator).isFalse() sessionVerificationService.givenCanVerifySession(false) - Truth.assertThat(awaitItem().showAvatarIndicator).isFalse() + assertThat(awaitItem().showAvatarIndicator).isFalse() encryptionService.emitBackupState(BackupState.UNKNOWN) val finalState = awaitItem() - Truth.assertThat(finalState.showAvatarIndicator).isTrue() + assertThat(finalState.showAvatarIndicator).isTrue() scope.cancel() } } @@ -131,9 +131,9 @@ class RoomListPresenterTests { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.matrixUser).isNull() + assertThat(initialState.matrixUser).isNull() val withUserState = awaitItem() - Truth.assertThat(withUserState.matrixUser).isNotNull() + assertThat(withUserState.matrixUser).isNotNull() scope.cancel() } } @@ -147,10 +147,10 @@ class RoomListPresenterTests { }.test { skipItems(1) val withUserState = awaitItem() - Truth.assertThat(withUserState.filter).isEqualTo("") + assertThat(withUserState.filter).isEqualTo("") withUserState.eventSink.invoke(RoomListEvents.UpdateFilter("t")) val withFilterState = awaitItem() - Truth.assertThat(withFilterState.filter).isEqualTo("t") + assertThat(withFilterState.filter).isEqualTo("t") cancelAndIgnoreRemainingEvents() scope.cancel() } @@ -169,12 +169,12 @@ class RoomListPresenterTests { }.test { val initialState = consumeItemsUntilPredicate { state -> state.roomList.size == 16 }.last() // Room list is loaded with 16 placeholders - Truth.assertThat(initialState.roomList.size).isEqualTo(16) - Truth.assertThat(initialState.roomList.all { it.isPlaceholder }).isTrue() + assertThat(initialState.roomList.size).isEqualTo(16) + assertThat(initialState.roomList.all { it.isPlaceholder }).isTrue() roomListService.postAllRooms(listOf(aRoomSummaryFilled())) val withRoomState = consumeItemsUntilPredicate { state -> state.roomList.size == 1 }.last() - Truth.assertThat(withRoomState.roomList.size).isEqualTo(1) - Truth.assertThat(withRoomState.roomList.first()) + assertThat(withRoomState.roomList.size).isEqualTo(1) + assertThat(withRoomState.roomList.first()) .isEqualTo(aRoomListRoomSummary) scope.cancel() } @@ -192,19 +192,24 @@ class RoomListPresenterTests { presenter.present() }.test { roomListService.postAllRooms(listOf(aRoomSummaryFilled())) - val loadedState = consumeItemsUntilPredicate { state -> state.roomList.size == 1 }.last() + skipItems(3) + val loadedState = awaitItem() // Test filtering with result + assertThat(loadedState.roomList.size).isEqualTo(1) loadedState.eventSink.invoke(RoomListEvents.UpdateFilter(A_ROOM_NAME.substring(0, 3))) - val withFilteredRoomState = consumeItemsUntilPredicate { state -> state.filteredRoomList.size == 1 }.last() - Truth.assertThat(withFilteredRoomState.filter).isEqualTo(A_ROOM_NAME.substring(0, 3)) - Truth.assertThat(withFilteredRoomState.filteredRoomList.size).isEqualTo(1) - Truth.assertThat(withFilteredRoomState.filteredRoomList.first()) + skipItems(1) + val withFilteredRoomState = awaitItem() + assertThat(withFilteredRoomState.filteredRoomList.size).isEqualTo(1) + assertThat(withFilteredRoomState.filter).isEqualTo(A_ROOM_NAME.substring(0, 3)) + assertThat(withFilteredRoomState.filteredRoomList.size).isEqualTo(1) + assertThat(withFilteredRoomState.filteredRoomList.first()) .isEqualTo(aRoomListRoomSummary) // Test filtering without result withFilteredRoomState.eventSink.invoke(RoomListEvents.UpdateFilter("tada")) - val withNotFilteredRoomState = consumeItemsUntilPredicate { state -> state.filteredRoomList.size == 0 }.last() - Truth.assertThat(withNotFilteredRoomState.filter).isEqualTo("tada") - Truth.assertThat(withNotFilteredRoomState.filteredRoomList).isEmpty() + skipItems(1) + val withNotFilteredRoomState = awaitItem() + assertThat(withNotFilteredRoomState.filter).isEqualTo("tada") + assertThat(withNotFilteredRoomState.filteredRoomList).isEmpty() scope.cancel() } } @@ -223,28 +228,28 @@ class RoomListPresenterTests { roomListService.postAllRooms(listOf(aRoomSummaryFilled())) val loadedState = awaitItem() // check initial value - Truth.assertThat(roomListService.latestSlidingSyncRange).isNull() + assertThat(roomListService.latestSlidingSyncRange).isNull() // Test empty range loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(1, 0))) - Truth.assertThat(roomListService.latestSlidingSyncRange).isNull() + assertThat(roomListService.latestSlidingSyncRange).isNull() // Update visible range and check that range is transmitted to the SDK after computation loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(0, 0))) - Truth.assertThat(roomListService.latestSlidingSyncRange) + assertThat(roomListService.latestSlidingSyncRange) .isEqualTo(IntRange(0, 20)) loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(0, 1))) - Truth.assertThat(roomListService.latestSlidingSyncRange) + assertThat(roomListService.latestSlidingSyncRange) .isEqualTo(IntRange(0, 21)) loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(19, 29))) - Truth.assertThat(roomListService.latestSlidingSyncRange) + assertThat(roomListService.latestSlidingSyncRange) .isEqualTo(IntRange(0, 49)) loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(49, 59))) - Truth.assertThat(roomListService.latestSlidingSyncRange) + assertThat(roomListService.latestSlidingSyncRange) .isEqualTo(IntRange(29, 79)) loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(149, 159))) - Truth.assertThat(roomListService.latestSlidingSyncRange) + assertThat(roomListService.latestSlidingSyncRange) .isEqualTo(IntRange(129, 179)) loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(149, 259))) - Truth.assertThat(roomListService.latestSlidingSyncRange) + assertThat(roomListService.latestSlidingSyncRange) .isEqualTo(IntRange(129, 279)) cancelAndIgnoreRemainingEvents() scope.cancel() @@ -270,10 +275,10 @@ class RoomListPresenterTests { presenter.present() }.test { val eventSink = awaitItem().eventSink - Truth.assertThat(awaitItem().displayVerificationPrompt).isTrue() + assertThat(awaitItem().displayVerificationPrompt).isTrue() eventSink(RoomListEvents.DismissRequestVerificationPrompt) - Truth.assertThat(awaitItem().displayVerificationPrompt).isFalse() + assertThat(awaitItem().displayVerificationPrompt).isFalse() scope.cancel() } } @@ -288,16 +293,16 @@ class RoomListPresenterTests { presenter.present() }.test { skipItems(1) - Truth.assertThat(awaitItem().invitesState).isEqualTo(InvitesState.NoInvites) + assertThat(awaitItem().invitesState).isEqualTo(InvitesState.NoInvites) inviteStateFlow.value = InvitesState.SeenInvites - Truth.assertThat(awaitItem().invitesState).isEqualTo(InvitesState.SeenInvites) + assertThat(awaitItem().invitesState).isEqualTo(InvitesState.SeenInvites) inviteStateFlow.value = InvitesState.NewInvites - Truth.assertThat(awaitItem().invitesState).isEqualTo(InvitesState.NewInvites) + assertThat(awaitItem().invitesState).isEqualTo(InvitesState.NewInvites) inviteStateFlow.value = InvitesState.NoInvites - Truth.assertThat(awaitItem().invitesState).isEqualTo(InvitesState.NoInvites) + assertThat(awaitItem().invitesState).isEqualTo(InvitesState.NoInvites) scope.cancel() } } @@ -316,7 +321,7 @@ class RoomListPresenterTests { initialState.eventSink(RoomListEvents.ShowContextMenu(summary)) val shownState = awaitItem() - Truth.assertThat(shownState.contextMenu) + assertThat(shownState.contextMenu) .isEqualTo(RoomListState.ContextMenu.Shown(summary.roomId, summary.name)) scope.cancel() } @@ -336,12 +341,12 @@ class RoomListPresenterTests { initialState.eventSink(RoomListEvents.ShowContextMenu(summary)) val shownState = awaitItem() - Truth.assertThat(shownState.contextMenu) + assertThat(shownState.contextMenu) .isEqualTo(RoomListState.ContextMenu.Shown(summary.roomId, summary.name)) shownState.eventSink(RoomListEvents.HideContextMenu) val hiddenState = awaitItem() - Truth.assertThat(hiddenState.contextMenu).isEqualTo(RoomListState.ContextMenu.Hidden) + assertThat(hiddenState.contextMenu).isEqualTo(RoomListState.ContextMenu.Hidden) scope.cancel() } } @@ -356,7 +361,7 @@ class RoomListPresenterTests { }.test { val initialState = awaitItem() initialState.eventSink(RoomListEvents.LeaveRoom(A_ROOM_ID)) - Truth.assertThat(leaveRoomPresenter.events).containsExactly(LeaveRoomEvent.ShowConfirmation(A_ROOM_ID)) + assertThat(leaveRoomPresenter.events).containsExactly(LeaveRoomEvent.ShowConfirmation(A_ROOM_ID)) cancelAndIgnoreRemainingEvents() scope.cancel() } @@ -384,7 +389,7 @@ class RoomListPresenterTests { }.last() val room = updatedState.roomList.find { it.id == A_ROOM_ID.value } - Truth.assertThat(room?.notificationMode).isEqualTo(userDefinedMode) + assertThat(room?.notificationMode).isEqualTo(userDefinedMode) cancelAndIgnoreRemainingEvents() scope.cancel() } diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSourceTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSourceTest.kt index ce7bf685a8..fc41e48b53 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSourceTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSourceTest.kt @@ -19,7 +19,7 @@ package io.element.android.features.roomlist.impl.datasource import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import io.element.android.features.invitelist.test.FakeSeenInvitesStore import io.element.android.features.roomlist.impl.InvitesState import io.element.android.libraries.matrix.test.A_ROOM_ID @@ -43,7 +43,7 @@ internal class DefaultInviteStateDataSourceTest { moleculeFlow(RecompositionMode.Immediate) { dataSource.inviteState() }.test { - Truth.assertThat(awaitItem()).isEqualTo(InvitesState.NoInvites) + assertThat(awaitItem()).isEqualTo(InvitesState.NoInvites) } } @@ -59,7 +59,7 @@ internal class DefaultInviteStateDataSourceTest { dataSource.inviteState() }.test { skipItems(1) - Truth.assertThat(awaitItem()).isEqualTo(InvitesState.NewInvites) + assertThat(awaitItem()).isEqualTo(InvitesState.NewInvites) } } @@ -76,7 +76,7 @@ internal class DefaultInviteStateDataSourceTest { dataSource.inviteState() }.test { skipItems(1) - Truth.assertThat(awaitItem()).isEqualTo(InvitesState.NewInvites) + assertThat(awaitItem()).isEqualTo(InvitesState.NewInvites) } } @@ -94,7 +94,7 @@ internal class DefaultInviteStateDataSourceTest { }.test { skipItems(1) - Truth.assertThat(awaitItem()).isEqualTo(InvitesState.SeenInvites) + assertThat(awaitItem()).isEqualTo(InvitesState.SeenInvites) } } @@ -109,27 +109,27 @@ internal class DefaultInviteStateDataSourceTest { dataSource.inviteState() }.test { // Initially there are no invites - Truth.assertThat(awaitItem()).isEqualTo(InvitesState.NoInvites) + assertThat(awaitItem()).isEqualTo(InvitesState.NoInvites) // When a single invite is received, state should be NewInvites roomListService.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID))) skipItems(1) - Truth.assertThat(awaitItem()).isEqualTo(InvitesState.NewInvites) + assertThat(awaitItem()).isEqualTo(InvitesState.NewInvites) // If that invite is marked as seen, then the state becomes SeenInvites seenStore.publishRoomIds(setOf(A_ROOM_ID)) skipItems(1) - Truth.assertThat(awaitItem()).isEqualTo(InvitesState.SeenInvites) + assertThat(awaitItem()).isEqualTo(InvitesState.SeenInvites) // Another new invite resets it to NewInvites roomListService.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(roomId = A_ROOM_ID_2))) skipItems(1) - Truth.assertThat(awaitItem()).isEqualTo(InvitesState.NewInvites) + assertThat(awaitItem()).isEqualTo(InvitesState.NewInvites) // All of the invites going away reverts to NoInvites roomListService.postInviteRooms(emptyList()) skipItems(1) - Truth.assertThat(awaitItem()).isEqualTo(InvitesState.NoInvites) + assertThat(awaitItem()).isEqualTo(InvitesState.NoInvites) } } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt index 0ce510336c..bc635e5e98 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt @@ -19,7 +19,6 @@ package io.element.android.features.securebackup.impl import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin @@ -33,8 +32,8 @@ import io.element.android.features.securebackup.impl.enable.SecureBackupEnableNo import io.element.android.features.securebackup.impl.enter.SecureBackupEnterRecoveryKeyNode import io.element.android.features.securebackup.impl.root.SecureBackupRootNode import io.element.android.features.securebackup.impl.setup.SecureBackupSetupNode -import io.element.android.libraries.architecture.BackstackNode -import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler +import io.element.android.libraries.architecture.BackstackView +import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope import kotlinx.parcelize.Parcelize @@ -43,7 +42,7 @@ import kotlinx.parcelize.Parcelize class SecureBackupFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, -) : BackstackNode( +) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Root, savedStateMap = buildContext.savedStateMap, @@ -124,10 +123,6 @@ class SecureBackupFlowNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { - Children( - navModel = backstack, - modifier = modifier, - transitionHandler = rememberDefaultTransitionHandler() - ) + BackstackView() } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt index cf3e6c48c5..4f515fd841 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt @@ -26,9 +26,13 @@ import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.securebackup.impl.R import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.atomic.pages.FlowStepPage @@ -40,7 +44,6 @@ import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme @Composable fun SecureBackupDisableView( @@ -59,7 +62,7 @@ fun SecureBackupDisableView( onBackClicked = onBackClicked, title = stringResource(id = R.string.screen_key_backup_disable_title), subTitle = stringResource(id = R.string.screen_key_backup_disable_description), - iconResourceId = CommonDrawables.ic_key_off, + iconVector = ImageVector.vectorResource(CommonDrawables.ic_key_off), content = { Content(state = state) }, buttons = { Buttons(state = state) }, ) @@ -119,7 +122,7 @@ private fun Content(state: SecureBackupDisableState) { private fun SecureBackupDisableItem(text: String) { Row(modifier = Modifier.fillMaxWidth()) { Icon( - resourceId = CommonDrawables.ic_compound_close, + imageVector = CompoundIcons.Close, contentDescription = null, tint = ElementTheme.colors.iconCriticalPrimary, modifier = Modifier.size(20.dp) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt index e90832c5be..d97fc735d8 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt @@ -21,7 +21,9 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.features.securebackup.impl.R import io.element.android.libraries.architecture.Async @@ -48,7 +50,7 @@ fun SecureBackupEnableView( modifier = modifier, onBackClicked = onBackClicked, title = stringResource(id = R.string.screen_chat_backup_key_backup_action_enable), - iconResourceId = CommonDrawables.ic_key, + iconVector = ImageVector.vectorResource(CommonDrawables.ic_key), buttons = { Buttons(state = state) } ) if (state.enableAction is Async.Failure) { diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenter.kt index bec4dd9217..9207dbef79 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenter.kt @@ -89,7 +89,7 @@ class SecureBackupEnterRecoveryKeyPresenter @Inject constructor( action: MutableState> ) = launch { suspend { - encryptionService.fixRecoveryIssues(recoveryKey).getOrThrow() + encryptionService.recover(recoveryKey).getOrThrow() }.runCatchingUpdatingState(action) } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt index a12b5907d2..d5eda2c15a 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt @@ -21,7 +21,9 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.securebackup.impl.R @@ -53,7 +55,7 @@ fun SecureBackupEnterRecoveryKeyView( FlowStepPage( modifier = modifier, onBackClicked = onBackClicked, - iconResourceId = CommonDrawables.ic_key, + iconVector = ImageVector.vectorResource(CommonDrawables.ic_key), title = stringResource(id = R.string.screen_recovery_key_confirm_title), subTitle = stringResource(id = R.string.screen_recovery_key_confirm_description), content = { Content(state = state) }, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt index 3a27ae553e..d4033f1297 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt @@ -41,7 +41,7 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.RecoveryState -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @Composable diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt index 1bba716bb1..eab2e0450d 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt @@ -129,11 +129,11 @@ class SecureBackupSetupPresenter @AssistedInject constructor( encryptionService.enableRecoveryProgressStateFlow.collect { enableRecoveryProgress -> Timber.tag(loggerTagSetup.value).d("New enableRecoveryProgress: ${enableRecoveryProgress.javaClass.simpleName}") when (enableRecoveryProgress) { - EnableRecoveryProgress.Unknown, + is EnableRecoveryProgress.Starting, + is EnableRecoveryProgress.CreatingBackup, + is EnableRecoveryProgress.CreatingRecoveryKey, is EnableRecoveryProgress.BackingUp, - EnableRecoveryProgress.CreatingBackup, - EnableRecoveryProgress.CreatingRecoveryKey -> - Unit + is EnableRecoveryProgress.RoomKeyUploadError -> Unit is EnableRecoveryProgress.Done -> stateAndDispatch.dispatchAction(SecureBackupSetupStateMachine.Event.SdkHasCreatedKey(enableRecoveryProgress.recoveryKey)) } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt index 83b1ddba9c..abdf8e9563 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt @@ -21,10 +21,13 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.securebackup.impl.R import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyView import io.element.android.libraries.androidutils.system.copyToClipboard @@ -51,7 +54,7 @@ fun SecureBackupSetupView( onBackClicked = onBackClicked.takeIf { state.canGoBack() }, title = title(state), subTitle = subtitle(state), - iconResourceId = CommonDrawables.ic_key, + iconVector = ImageVector.vectorResource(CommonDrawables.ic_key), content = { Content(state) }, buttons = { Buttons(state, onDone = onDone) }, ) @@ -154,7 +157,7 @@ private fun ColumnScope.Buttons( is SetupState.CreatedAndSaved -> { OutlinedButton( text = stringResource(id = R.string.screen_recovery_key_save_action), - leadingIcon = IconSource.Resource(CommonDrawables.ic_compound_download), + leadingIcon = IconSource.Vector(CompoundIcons.Download), modifier = Modifier.fillMaxWidth(), onClick = { context.startSharePlainTextIntent( diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt index 7b722dd32b..34b731b1ab 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt @@ -52,7 +52,8 @@ import io.element.android.libraries.designsystem.theme.components.OutlinedTextFi import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.autofill import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -199,7 +200,7 @@ private fun RecoveryKeyFooter(state: RecoveryKeyViewState) { verticalAlignment = Alignment.CenterVertically, ) { Icon( - resourceId = CommonDrawables.ic_compound_info_solid, + imageVector = CompoundIcons.InfoSolid, contentDescription = null, tint = ElementTheme.colors.iconSecondary, modifier = Modifier diff --git a/features/securebackup/impl/src/main/res/values-de/translations.xml b/features/securebackup/impl/src/main/res/values-de/translations.xml new file mode 100644 index 0000000000..77ec0fa82a --- /dev/null +++ b/features/securebackup/impl/src/main/res/values-de/translations.xml @@ -0,0 +1,43 @@ + + + "Backup deaktivieren" + "Backup aktivieren" + "Das Backup stellt sicher, dass du deinen Nachrichtenverlauf nicht verlierst. %1$s." + "Backup" + "Wiederherstellungsschlüssel ändern" + "Wiederherstellungsschlüssel bestätigen" + "Dein Chat-Backup ist derzeit nicht synchronisiert." + "Wiederherstellung einrichten" + "Erhalte Zugriff auf deine verschlüsselten Nachrichten, wenn du alle deine Geräte verlierst oder von %1$s überall abgemeldet bist." + "Ausschalten" + "Du verlierst deine verschlüsselten Nachrichten, wenn du auf allen Geräten abgemeldet bist." + "Bist du sicher, dass du das Backup ausschalten willst?" + "Wenn du das Backup ausschaltest, wird dein aktuelles Backup des Verschlüsselungsschlüssels entfernt und andere Sicherheitsfunktionen werden deaktiviert. In diesem Fall wirst du:" + "Kein verschlüsselter Nachrichtenverlauf auf neuen Geräten" + "Du verlierst den Zugriff auf deine verschlüsselten Nachrichten, wenn du dich überall von %1$s abmeldest" + "Bist du sicher, dass du das Backup ausschalten willst?" + "Besorge dir einen neuen Wiederherstellungsschlüssel, wenn du deinen alten verloren hast. Nachdem du deinen Wiederherstellungsschlüssel geändert hast, funktioniert dein alter Schlüssel nicht mehr." + "Stelle sicher, dass du deinen Wiederherstellungsschlüssel an einem sicheren Ort aufbewahren kannst" + "Wiederherstellungsschlüssel geändert" + "Wiederherstellungsschlüssel ändern?" + "Gib deinen Wiederherstellungsschlüssel ein, um den Zugriff auf dein Chat-Backup zu bestätigen." + "Bitte versuche es noch einmal, um den Zugriff auf dein Chat-Backup zu bestätigen." + "Falscher Wiederherstellungsschlüssel" + "Gib den 48-stelligen Code ein." + "Eingeben…" + "Wiederherstellungsschlüssel bestätigt" + "Wiederherstellungsschlüssel bestätigen." + "Wiederherstellungsschlüssel kopiert" + "Generieren…" + "Wiederherstellungsschlüssel speichern" + "Notiere dir deinen Wiederherstellungsschlüssel an einem sicheren Ort oder speichere ihn in einem Passwort-Manager." + "Tippe, um den Wiederherstellungsschlüssel zu kopieren" + "Speichere deinen Wiederherstellungsschlüssel" + "Nach diesem Schritt kannst du nicht mehr auf deinen neuen Wiederherstellungsschlüssel zugreifen." + "Hast du deinen Wiederherstellungsschlüssel gespeichert?" + "Dein Chat-Backup ist durch einen Wiederherstellungsschlüssel geschützt. Wenn du nach der Einrichtung einen neuen Wiederherstellungsschlüssel brauchst, kannst du ihn über die Option \"Wiederherstellungsschlüssel ändern\" neu erstellen." + "Wiederherstellungsschlüssel erstellen" + "Stelle sicher, dass du deinen Wiederherstellungsschlüssel an einem sicheren Ort aufbewahren kannst" + "Einrichtung der Wiederherstellung erfolgreich" + "Wiederherstellung einrichten" + diff --git a/features/securebackup/impl/src/main/res/values-fr/translations.xml b/features/securebackup/impl/src/main/res/values-fr/translations.xml index 3922f2fa78..9f3f304b25 100644 --- a/features/securebackup/impl/src/main/res/values-fr/translations.xml +++ b/features/securebackup/impl/src/main/res/values-fr/translations.xml @@ -22,6 +22,8 @@ "Clé de récupération modifée" "Changer la clé de récupération?" "Saisissez votre clé de récupération pour accéder à l’historique de vos discussions." + "Veuillez réessayer pour accéder à votre historique chiffré." + "Clé de récupération incorrecte" "Saisissez la clé à 48 caractères." "Saisissez la clé ici…" "Clé de récupération confirmée" diff --git a/features/securebackup/impl/src/main/res/values-hu/translations.xml b/features/securebackup/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..0eff260322 --- /dev/null +++ b/features/securebackup/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,44 @@ + + + "Biztonsági mentés kikapcsolása" + "Biztonsági mentés bekapcsolása" + "A biztonsági mentés biztosítja, hogy ne veszítse el az üzenetelőzményeit. %1$s." + "Biztonsági mentés" + "Helyreállítási kulcs módosítása" + "Helyreállítási kulcs megerősítése" + "A csevegéselőzményei nincsenek szinkronban." + "Helyreállítás beállítása" + "Szerezzen hozzáférést a titkosított üzeneteihez, ha elvesztette az összes eszközét, vagy ha mindenütt kijelentkezett az %1$sből." + "Kikapcsolás" + "Ha kijelentkezik az összes eszközéről, akkor elveszti a titkosított üzeneteit." + "Biztos, hogy kikapcsolja a biztonsági mentéseket?" + "A biztonsági mentés kikapcsolása eltávolítja a jelenlegi titkosítási kulcsának mentését, és kikapcsol más biztonsági funkciókat is. Ebben az esetben:" + "Nem lesznek meg a titkosított üzenetelőzményei az új eszközein" + "Elveszti a titkosított üzenetei hozzáférését, ha mindenhol kilép az %1$sből" + "Biztos, hogy kikapcsolja a biztonsági mentéseket?" + "Szerezzen új helyreállítási kulcsot, ha elvesztette a meglévőt. A helyreállítása kulcsa módosítása után a régi már nem fog működni." + "Új helyreállítási kulcs előállítása" + "Gondoskodjon arról, hogy biztonságos helyen tárolja a helyreállítási kulcsát" + "Helyreállítási kulcs lecserélve" + "Módosítja a helyreállítási kulcsot?" + "Adja meg a helyreállítási kulcsát, hogy megerősítse a csevegések biztonsági mentéséhez való hozzáférését." + "Próbálja meg újra megerősíteni a csevegés biztonsági mentéséhez való hozzáférését." + "Helytelen helyreállítási kulcs" + "Adja meg a 48 karakteres kódot." + "Megadás…" + "Helyreállítási kulcs megerősítve" + "Erősítse meg a helyreállítási kulcsát" + "Helyreállítási kulcs másolva" + "Előállítás…" + "Helyreállítási kulcs mentése" + "Írja le a helyreállítási kulcsát valami biztonságos helyre, vagy mentse egy jelszókezelőbe." + "Koppintson a helyreállítási kulcs másolásához" + "Mentse el a helyreállítási kulcsát" + "Ezután a lépés után nem fog tudni hozzáférni az új helyreállítási kulcsához." + "Mentette a helyreállítási kulcsát?" + "A csevegései biztonsági mentését a helyreállítási kulcsa védi. Ha új helyreállítási kulcsra van szüksége a beállítás után, akkor a „Helyreállítási kulcs módosítása” választásával újból létrehozhat egyet." + "Helyreállítási kulcs előállítása" + "Gondoskodjon arról, hogy biztonságos helyen tárolja a helyreállítási kulcsát" + "A helyreállítás beállítása sikeres" + "Helyreállítás beállítása" + diff --git a/features/securebackup/impl/src/main/res/values-in/translations.xml b/features/securebackup/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..c7712832d2 --- /dev/null +++ b/features/securebackup/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,44 @@ + + + "Matikan pencadangan" + "Nyalakan pencadangan" + "Pencadangan memastikan bahwa Anda tidak akan kehilangan riwayat pesan Anda. %1$s." + "Pencadangan" + "Ubah kunci pemulihan" + "Konfirmasi kunci pemulihan" + "Pencadangan percakapan Anda saat ini tidak tersinkron." + "Siapkan pemulihan" + "Dapatkan akses ke pesan terenkripsi Anda jika Anda kehilangan semua perangkat Anda atau keluar dari %1$s di mana pun." + "Matikan" + "Anda akan kehilangan pesan terenkripsi jika Anda keluar dari semua perangkat." + "Apakah Anda yakin ingin mematikan pencadangan?" + "Mematikan pencadangan akan menghapus pencadangan kunci terenkripsi saat ini dan mematikan fitur keamanan lainnya. Dalam hal ini, Anda akan:" + "Tidak memiliki riwayat pesan terenkripsi di perangkat baru" + "Kehilangan akses ke pesan terenkripsi Anda jika keluar dari %1$s di mana pun" + "Apakah Anda yakin ingin mematikan pencadangan?" + "Dapatkan kunci pemulihan yang baru jika Anda kehilangan kunci pemulihan saat ini. Setelah mengganti kunci pemulihan Anda, yang lama tidak akan bekerja lagi." + "Buat kunci pemulihan baru" + "Pastikan Anda dapat menyimpan kunci pemulihan Anda di tempat yang aman" + "Kunci pemulihan diganti" + "Ubah kunci pemulihan?" + "Masukkan kunci pemulihan Anda untuk mengonfirmasi akses ke cadangan percakapan Anda." + "Silakan coba lagi untuk mengonfirmasi akses ke cadangan percakapan Anda." + "Kunci pemulihan salah" + "Masukkan kode 48 karakter." + "Masukkan…" + "Kunci pemulihan dikonfirmasi" + "Konfirmasi kunci pemulihan Anda" + "Kunci pemulihan disalin" + "Membuat…" + "Simpan kunci pemulihan" + "Tuliskan kunci pemulihan Anda di tempat yang aman atau simpan di pengelola kata sandi." + "Ketuk untuk menyalin kunci pemulihan" + "Simpan kunci pemulihan Anda" + "Anda tidak akan dapat mengakses kunci pemulihan Anda setelah langkah ini." + "Apakah Anda sudah menyimpan kunci pemulihan Anda?" + "Pencadangan percakapan Anda sedang dilindungi oleh sebuah kunci pemulihan. Jika Anda perlu kunci pemulihan yang baru setelah penyiapan, Anda dapat membuat ulang dengan memilih \'Ubah kunci pemulihan\'." + "Buat kunci pemulihan Anda" + "Pastikan Anda dapat menyimpan kunci pemulihan Anda di tempat yang aman" + "Penyiapan pemulihan berhasil" + "Siapkan pemulihan" + diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenterTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenterTest.kt index d34f869f6e..862cbbde72 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenterTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenterTest.kt @@ -73,7 +73,7 @@ class SecureBackupEnterRecoveryKeyPresenterTest { inProgress = false, ) ) - encryptionService.givenFixRecoveryIssuesFailure(AN_EXCEPTION) + encryptionService.givenRecoverFailure(AN_EXCEPTION) withRecoveryKeyState.eventSink(SecureBackupEnterRecoveryKeyEvents.Submit) val loadingState = awaitItem() assertThat(loadingState.submitAction).isEqualTo(Async.Loading()) @@ -85,7 +85,7 @@ class SecureBackupEnterRecoveryKeyPresenterTest { val clearedState = awaitItem() assertThat(clearedState.submitAction).isEqualTo(Async.Uninitialized) assertThat(clearedState.isSubmitEnabled).isTrue() - encryptionService.givenFixRecoveryIssuesFailure(null) + encryptionService.givenRecoverFailure(null) clearedState.eventSink(SecureBackupEnterRecoveryKeyEvents.Submit) val loadingState2 = awaitItem() assertThat(loadingState2.submitAction).isEqualTo(Async.Loading()) diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt index 8efabcf945..66d552423e 100644 --- a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt +++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt @@ -41,7 +41,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.temporaryColorBgSpecial import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.persistentListOf diff --git a/features/signedout/impl/src/main/res/values-de/translations.xml b/features/signedout/impl/src/main/res/values-de/translations.xml new file mode 100644 index 0000000000..08e0305f8e --- /dev/null +++ b/features/signedout/impl/src/main/res/values-de/translations.xml @@ -0,0 +1,8 @@ + + + "Du hast dein Passwort in einer anderen Sitzung geändert" + "Du hast die Sitzung aus einer anderen Sitzung gelöscht" + "Der Administrator deines Servers hat deinen Zugang ungültig gemacht" + "Möglicherweise wurdest du aus einem der unten aufgeführten Gründe abgemeldet. Bitte melde dich erneut an, um %s weiter zu nutzen." + "Du bist abgemeldet" + diff --git a/features/signedout/impl/src/main/res/values-hu/translations.xml b/features/signedout/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..a73be9ea55 --- /dev/null +++ b/features/signedout/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,8 @@ + + + "Megváltoztatta a jelszavát egy másik munkamenetben" + "Törölte a munkamenetet egy másik munkamenetből" + "A kiszolgáló rendszergazdája érvénytelenítette a hozzáférését" + "Előfordulhat, hogy az alábbiakban felsorolt okok valamelyike miatt került kijelentkezésre. Kérjük, jelentkezzen be újra, hogy folytatni tudja a %s használatát." + "Kijelentkezett" + diff --git a/features/signedout/impl/src/main/res/values-in/translations.xml b/features/signedout/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..7feb4b4edb --- /dev/null +++ b/features/signedout/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,8 @@ + + + "Anda telah mengubah kata sandi Anda di sesi yang lain" + "Anda telah menghapus sesi dari sesi lain" + "Administrator homeserver Anda telah membatalkan akses Anda" + "Anda mungkin dikeluarkan karena alasan berikut. Silakan masuk lagi untuk melanjutkan menggunakan %s." + "Anda telah keluar" + diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt index 689ff3a154..89751ca604 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt @@ -97,7 +97,7 @@ class VerifySelfSessionPresenter @Inject constructor( is StateMachineState.Verifying.Replying -> Async.Loading() else -> Async.Uninitialized } - VerifySelfSessionState.VerificationStep.Verifying(machineState.emojis, async) + VerifySelfSessionState.VerificationStep.Verifying(machineState.data, async) } StateMachineState.Completed -> { @@ -116,7 +116,7 @@ class VerifySelfSessionPresenter @Inject constructor( stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidStartSasVerification) } is VerificationFlowState.ReceivedVerificationData -> { - stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidReceiveChallenge(verificationAttemptState.emoji)) + stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidReceiveChallenge(verificationAttemptState.data)) } VerificationFlowState.Finished -> { stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidAcceptChallenge) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt index 248e3aec10..f3afd3b7b9 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt @@ -19,7 +19,7 @@ package io.element.android.features.verifysession.impl import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable import io.element.android.libraries.architecture.Async -import io.element.android.libraries.matrix.api.verification.VerificationEmoji +import io.element.android.libraries.matrix.api.verification.SessionVerificationData @Immutable data class VerifySelfSessionState( @@ -33,7 +33,7 @@ data class VerifySelfSessionState( data object Canceled : VerificationStep data object AwaitingOtherDeviceResponse : VerificationStep data object Ready : VerificationStep - data class Verifying(val emojiList: List, val state: Async) : VerificationStep + data class Verifying(val data: SessionVerificationData, val state: Async) : VerificationStep data object Completed : VerificationStep } } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateMachine.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateMachine.kt index ad48294e92..52bd38bfbd 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateMachine.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateMachine.kt @@ -20,8 +20,8 @@ package io.element.android.features.verifysession.impl import com.freeletics.flowredux.dsl.FlowReduxStateMachine +import io.element.android.libraries.matrix.api.verification.SessionVerificationData import io.element.android.libraries.matrix.api.verification.SessionVerificationService -import io.element.android.libraries.matrix.api.verification.VerificationEmoji import kotlinx.coroutines.ExperimentalCoroutinesApi import javax.inject.Inject import com.freeletics.flowredux.dsl.State as MachineState @@ -70,15 +70,15 @@ class VerifySelfSessionStateMachine @Inject constructor( } inState { on { event: Event.DidReceiveChallenge, state: MachineState -> - state.override { State.Verifying.ChallengeReceived(event.emojis) } + state.override { State.Verifying.ChallengeReceived(event.data) } } } inState { on { _: Event.AcceptChallenge, state: MachineState -> - state.override { State.Verifying.Replying(state.snapshot.emojis, accept = true) } + state.override { State.Verifying.Replying(state.snapshot.data, accept = true) } } on { _: Event.DeclineChallenge, state: MachineState -> - state.override { State.Verifying.Replying(state.snapshot.emojis, accept = false) } + state.override { State.Verifying.Replying(state.snapshot.data, accept = false) } } } inState { @@ -139,12 +139,12 @@ class VerifySelfSessionStateMachine @Inject constructor( /** A SaS verification flow has been started. */ data object SasVerificationStarted : State - sealed class Verifying(open val emojis: List) : State { + sealed class Verifying(open val data: SessionVerificationData) : State { /** Verification accepted and emojis received. */ - data class ChallengeReceived(override val emojis: List) : Verifying(emojis) + data class ChallengeReceived(override val data: SessionVerificationData) : Verifying(data) /** Replying to a verification challenge. */ - data class Replying(override val emojis: List, val accept: Boolean) : Verifying(emojis) + data class Replying(override val data: SessionVerificationData, val accept: Boolean) : Verifying(data) } /** The verification is being canceled. */ @@ -170,8 +170,8 @@ class VerifySelfSessionStateMachine @Inject constructor( /** Started a SaS verification flow. */ data object DidStartSasVerification : Event - /** Has received emojis. */ - data class DidReceiveChallenge(val emojis: List) : Event + /** Has received data. */ + data class DidReceiveChallenge(val data: SessionVerificationData) : Event /** Emojis match. */ data object AcceptChallenge : Event diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt index 54a59dee01..9115b8b444 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt @@ -18,6 +18,7 @@ package io.element.android.features.verifysession.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.api.verification.SessionVerificationData import io.element.android.libraries.matrix.api.verification.VerificationEmoji open class VerifySelfSessionStateProvider : PreviewParameterProvider { @@ -28,10 +29,10 @@ open class VerifySelfSessionStateProvider : PreviewParameterProvider = aVerificationEmojiList(), +): SessionVerificationData { + return SessionVerificationData.Emojis(emojiList) +} + +private fun aDecimalsSessionVerificationData( + decimals: List = listOf(123, 456, 789), +): SessionVerificationData { + return SessionVerificationData.Decimals(decimals) +} + +private fun aVerifySelfSessionState() = VerifySelfSessionState( verificationFlowStep = VerifySelfSessionState.VerificationStep.Initial, eventSink = {}, ) -fun aVerificationEmojiList() = listOf( - VerificationEmoji("🍕", "Pizza"), - VerificationEmoji("🚀", "Rocket"), - VerificationEmoji("🚀", "Rocket"), - VerificationEmoji("🗺️", "Map"), - VerificationEmoji("🎳", "Bowling"), - VerificationEmoji("🎳", "Bowling"), - VerificationEmoji("📌", "Pin"), +private fun aVerificationEmojiList() = listOf( + VerificationEmoji(number = 27, emoji = "🍕", description = "Pizza"), + VerificationEmoji(number = 54, emoji = "🚀", description = "Rocket"), + VerificationEmoji(number = 54, emoji = "🚀", description = "Rocket"), + VerificationEmoji(number = 42, emoji = "📕", description = "Book"), + VerificationEmoji(number = 48, emoji = "🔨", description = "Hammer"), + VerificationEmoji(number = 48, emoji = "🔨", description = "Hammer"), + VerificationEmoji(number = 63, emoji = "📌", description = "Pin"), ) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt index 3bda19f75a..a132431812 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt @@ -17,6 +17,7 @@ package io.element.android.features.verifysession.impl import androidx.activity.compose.BackHandler +import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -25,6 +26,7 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.widthIn import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable @@ -33,11 +35,14 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp +import io.element.android.compound.theme.ElementTheme +import io.element.android.features.verifysession.impl.emoji.toEmojiResource import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule @@ -48,8 +53,8 @@ import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton +import io.element.android.libraries.matrix.api.verification.SessionVerificationData import io.element.android.libraries.matrix.api.verification.VerificationEmoji -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.features.verifysession.impl.VerifySelfSessionState.VerificationStep as FlowStep @@ -100,14 +105,23 @@ private fun HeaderContent(verificationFlowStep: FlowStep, modifier: Modifier = M FlowStep.Initial -> R.string.screen_session_verification_open_existing_session_title FlowStep.Canceled -> CommonStrings.common_verification_cancelled FlowStep.AwaitingOtherDeviceResponse -> R.string.screen_session_verification_waiting_to_accept_title - FlowStep.Ready, is FlowStep.Verifying, FlowStep.Completed -> R.string.screen_session_verification_compare_emojis_title + FlowStep.Ready, + FlowStep.Completed -> R.string.screen_session_verification_compare_emojis_title + is FlowStep.Verifying -> when (verificationFlowStep.data) { + is SessionVerificationData.Decimals -> R.string.screen_session_verification_compare_numbers_title + is SessionVerificationData.Emojis -> R.string.screen_session_verification_compare_emojis_title + } } val subtitleTextId = when (verificationFlowStep) { FlowStep.Initial -> R.string.screen_session_verification_open_existing_session_subtitle FlowStep.Canceled -> R.string.screen_session_verification_cancelled_subtitle FlowStep.AwaitingOtherDeviceResponse -> R.string.screen_session_verification_waiting_to_accept_subtitle - is FlowStep.Verifying, FlowStep.Completed -> R.string.screen_session_verification_compare_emojis_subtitle FlowStep.Ready -> R.string.screen_session_verification_ready_subtitle + FlowStep.Completed -> R.string.screen_session_verification_compare_emojis_subtitle + is FlowStep.Verifying -> when (verificationFlowStep.data) { + is SessionVerificationData.Decimals -> R.string.screen_session_verification_compare_numbers_subtitle + is SessionVerificationData.Emojis -> R.string.screen_session_verification_compare_emojis_subtitle + } } IconTitleSubtitleMolecule( @@ -138,17 +152,30 @@ private fun ContentWaiting(modifier: Modifier = Modifier) { @Composable private fun ContentVerifying(verificationFlowStep: FlowStep.Verifying, modifier: Modifier = Modifier) { - // We want each row to have up to 4 emojis - val rows = verificationFlowStep.emojiList.chunked(4) - Column(modifier = modifier.fillMaxWidth()) { - for ((rowIndex, emojis) in rows.withIndex()) { - // Vertical spacing between rows - if (rowIndex > 0) { - Spacer(modifier = Modifier.height(40.dp)) - } - Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) { - for (emoji in emojis) { - EmojiItemView(emoji = emoji, modifier = Modifier.widthIn(max = 60.dp)) + when (verificationFlowStep.data) { + is SessionVerificationData.Decimals -> { + val text = verificationFlowStep.data.decimals.joinToString(separator = " - ") { it.toString() } + Text( + modifier = modifier.fillMaxWidth(), + text = text, + style = ElementTheme.typography.fontHeadingLgBold, + color = MaterialTheme.colorScheme.primary, + textAlign = TextAlign.Center, + ) + } + is SessionVerificationData.Emojis -> { + // We want each row to have up to 4 emojis + val rows = verificationFlowStep.data.emojis.chunked(4) + Column( + modifier = modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(40.dp), + ) { + rows.forEach { emojis -> + Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) { + for (emoji in emojis) { + EmojiItemView(emoji = emoji, modifier = Modifier.widthIn(max = 60.dp)) + } + } } } } @@ -157,14 +184,16 @@ private fun ContentVerifying(verificationFlowStep: FlowStep.Verifying, modifier: @Composable private fun EmojiItemView(emoji: VerificationEmoji, modifier: Modifier = Modifier) { + val emojiResource = emoji.number.toEmojiResource() Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier) { - Text( - text = emoji.code, - style = ElementTheme.typography.fontBodyMdRegular.copy(fontSize = 34.sp), + Image( + modifier = Modifier.size(48.dp), + painter = painterResource(id = emojiResource.drawableRes), + contentDescription = null, ) Spacer(modifier = Modifier.height(16.dp)) Text( - emoji.name, + text = stringResource(id = emojiResource.nameRes), style = ElementTheme.typography.fontBodyMdRegular, color = MaterialTheme.colorScheme.secondary, maxLines = 1, diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/emoji/EmojiResource.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/emoji/EmojiResource.kt new file mode 100644 index 0000000000..e57239d5f2 --- /dev/null +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/emoji/EmojiResource.kt @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.verifysession.impl.emoji + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import io.element.android.features.verifysession.impl.R + +internal data class EmojiResource( + @DrawableRes val drawableRes: Int, + @StringRes val nameRes: Int +) + +internal fun Int.toEmojiResource(): EmojiResource { + return when (this % 64) { + 0 -> EmojiResource(R.drawable.ic_verification_00, R.string.verification_emoji_00) + 1 -> EmojiResource(R.drawable.ic_verification_01, R.string.verification_emoji_01) + 2 -> EmojiResource(R.drawable.ic_verification_02, R.string.verification_emoji_02) + 3 -> EmojiResource(R.drawable.ic_verification_03, R.string.verification_emoji_03) + 4 -> EmojiResource(R.drawable.ic_verification_04, R.string.verification_emoji_04) + 5 -> EmojiResource(R.drawable.ic_verification_05, R.string.verification_emoji_05) + 6 -> EmojiResource(R.drawable.ic_verification_06, R.string.verification_emoji_06) + 7 -> EmojiResource(R.drawable.ic_verification_07, R.string.verification_emoji_07) + 8 -> EmojiResource(R.drawable.ic_verification_08, R.string.verification_emoji_08) + 9 -> EmojiResource(R.drawable.ic_verification_09, R.string.verification_emoji_09) + 10 -> EmojiResource(R.drawable.ic_verification_10, R.string.verification_emoji_10) + 11 -> EmojiResource(R.drawable.ic_verification_11, R.string.verification_emoji_11) + 12 -> EmojiResource(R.drawable.ic_verification_12, R.string.verification_emoji_12) + 13 -> EmojiResource(R.drawable.ic_verification_13, R.string.verification_emoji_13) + 14 -> EmojiResource(R.drawable.ic_verification_14, R.string.verification_emoji_14) + 15 -> EmojiResource(R.drawable.ic_verification_15, R.string.verification_emoji_15) + 16 -> EmojiResource(R.drawable.ic_verification_16, R.string.verification_emoji_16) + 17 -> EmojiResource(R.drawable.ic_verification_17, R.string.verification_emoji_17) + 18 -> EmojiResource(R.drawable.ic_verification_18, R.string.verification_emoji_18) + 19 -> EmojiResource(R.drawable.ic_verification_19, R.string.verification_emoji_19) + 20 -> EmojiResource(R.drawable.ic_verification_20, R.string.verification_emoji_20) + 21 -> EmojiResource(R.drawable.ic_verification_21, R.string.verification_emoji_21) + 22 -> EmojiResource(R.drawable.ic_verification_22, R.string.verification_emoji_22) + 23 -> EmojiResource(R.drawable.ic_verification_23, R.string.verification_emoji_23) + 24 -> EmojiResource(R.drawable.ic_verification_24, R.string.verification_emoji_24) + 25 -> EmojiResource(R.drawable.ic_verification_25, R.string.verification_emoji_25) + 26 -> EmojiResource(R.drawable.ic_verification_26, R.string.verification_emoji_26) + 27 -> EmojiResource(R.drawable.ic_verification_27, R.string.verification_emoji_27) + 28 -> EmojiResource(R.drawable.ic_verification_28, R.string.verification_emoji_28) + 29 -> EmojiResource(R.drawable.ic_verification_29, R.string.verification_emoji_29) + 30 -> EmojiResource(R.drawable.ic_verification_30, R.string.verification_emoji_30) + 31 -> EmojiResource(R.drawable.ic_verification_31, R.string.verification_emoji_31) + 32 -> EmojiResource(R.drawable.ic_verification_32, R.string.verification_emoji_32) + 33 -> EmojiResource(R.drawable.ic_verification_33, R.string.verification_emoji_33) + 34 -> EmojiResource(R.drawable.ic_verification_34, R.string.verification_emoji_34) + 35 -> EmojiResource(R.drawable.ic_verification_35, R.string.verification_emoji_35) + 36 -> EmojiResource(R.drawable.ic_verification_36, R.string.verification_emoji_36) + 37 -> EmojiResource(R.drawable.ic_verification_37, R.string.verification_emoji_37) + 38 -> EmojiResource(R.drawable.ic_verification_38, R.string.verification_emoji_38) + 39 -> EmojiResource(R.drawable.ic_verification_39, R.string.verification_emoji_39) + 40 -> EmojiResource(R.drawable.ic_verification_40, R.string.verification_emoji_40) + 41 -> EmojiResource(R.drawable.ic_verification_41, R.string.verification_emoji_41) + 42 -> EmojiResource(R.drawable.ic_verification_42, R.string.verification_emoji_42) + 43 -> EmojiResource(R.drawable.ic_verification_43, R.string.verification_emoji_43) + 44 -> EmojiResource(R.drawable.ic_verification_44, R.string.verification_emoji_44) + 45 -> EmojiResource(R.drawable.ic_verification_45, R.string.verification_emoji_45) + 46 -> EmojiResource(R.drawable.ic_verification_46, R.string.verification_emoji_46) + 47 -> EmojiResource(R.drawable.ic_verification_47, R.string.verification_emoji_47) + 48 -> EmojiResource(R.drawable.ic_verification_48, R.string.verification_emoji_48) + 49 -> EmojiResource(R.drawable.ic_verification_49, R.string.verification_emoji_49) + 50 -> EmojiResource(R.drawable.ic_verification_50, R.string.verification_emoji_50) + 51 -> EmojiResource(R.drawable.ic_verification_51, R.string.verification_emoji_51) + 52 -> EmojiResource(R.drawable.ic_verification_52, R.string.verification_emoji_52) + 53 -> EmojiResource(R.drawable.ic_verification_53, R.string.verification_emoji_53) + 54 -> EmojiResource(R.drawable.ic_verification_54, R.string.verification_emoji_54) + 55 -> EmojiResource(R.drawable.ic_verification_55, R.string.verification_emoji_55) + 56 -> EmojiResource(R.drawable.ic_verification_56, R.string.verification_emoji_56) + 57 -> EmojiResource(R.drawable.ic_verification_57, R.string.verification_emoji_57) + 58 -> EmojiResource(R.drawable.ic_verification_58, R.string.verification_emoji_58) + 59 -> EmojiResource(R.drawable.ic_verification_59, R.string.verification_emoji_59) + 60 -> EmojiResource(R.drawable.ic_verification_60, R.string.verification_emoji_60) + 61 -> EmojiResource(R.drawable.ic_verification_61, R.string.verification_emoji_61) + 62 -> EmojiResource(R.drawable.ic_verification_62, R.string.verification_emoji_62) + /* 63 */ else -> EmojiResource(R.drawable.ic_verification_63, R.string.verification_emoji_63) + } +} diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/emoji/SasEmojisPreview.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/emoji/SasEmojisPreview.kt new file mode 100644 index 0000000000..0508aa66c3 --- /dev/null +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/emoji/SasEmojisPreview.kt @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.verifysession.impl.emoji + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.Text + +@Composable +@PreviewsDayNight +internal fun SasEmojisPreview() = ElementPreview { + Column( + modifier = Modifier.padding(8.dp), + verticalArrangement = Arrangement.spacedBy(2.dp), + ) { + List(64) { it to it.toEmojiResource() } + .chunked(8) + .forEach { + Row( + horizontalArrangement = Arrangement.spacedBy(2.dp), + ) { + it.forEach { emoji -> + Column( + modifier = Modifier.weight(1f), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Image( + painter = painterResource(id = emoji.second.drawableRes), + contentDescription = null, + modifier = Modifier + .size(32.dp) + ) + Text( + text = emoji.first.toString() + ":" + stringResource(id = emoji.second.nameRes), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = ElementTheme.typography.fontBodySmRegular.copy( + fontSize = 8.sp + ) + ) + } + } + } + } + } +} diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_00.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_00.xml new file mode 100644 index 0000000000..8346a5ebee --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_00.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_01.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_01.xml new file mode 100644 index 0000000000..b34cf63d98 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_01.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_02.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_02.xml new file mode 100644 index 0000000000..b97a508fc2 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_02.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_03.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_03.xml new file mode 100644 index 0000000000..bedf0f6f46 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_03.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_04.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_04.xml new file mode 100644 index 0000000000..19cef5d339 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_04.xml @@ -0,0 +1,30 @@ + + + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_05.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_05.xml new file mode 100644 index 0000000000..c31bd06c52 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_05.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_06.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_06.xml new file mode 100644 index 0000000000..d0a2de42cb --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_06.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_07.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_07.xml new file mode 100644 index 0000000000..c8ff75c999 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_07.xml @@ -0,0 +1,27 @@ + + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_08.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_08.xml new file mode 100644 index 0000000000..ab1e718c44 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_08.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_09.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_09.xml new file mode 100644 index 0000000000..cb7ad563f0 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_09.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_10.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_10.xml new file mode 100644 index 0000000000..fb2e05760f --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_10.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_11.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_11.xml new file mode 100644 index 0000000000..1cedc1b6ad --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_11.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_12.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_12.xml new file mode 100644 index 0000000000..30907f2496 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_12.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_13.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_13.xml new file mode 100644 index 0000000000..054760f3b8 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_13.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_14.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_14.xml new file mode 100644 index 0000000000..d4b557a7ed --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_14.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_15.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_15.xml new file mode 100644 index 0000000000..8a91221a80 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_15.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_16.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_16.xml new file mode 100644 index 0000000000..c5acc19a72 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_16.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_17.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_17.xml new file mode 100644 index 0000000000..ce8aff0657 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_17.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_18.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_18.xml new file mode 100644 index 0000000000..72f7036856 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_18.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_19.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_19.xml new file mode 100644 index 0000000000..2a07829cb3 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_19.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_20.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_20.xml new file mode 100644 index 0000000000..3f5abe6ae3 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_20.xml @@ -0,0 +1,12 @@ + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_21.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_21.xml new file mode 100644 index 0000000000..d390bd6e87 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_21.xml @@ -0,0 +1,12 @@ + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_22.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_22.xml new file mode 100644 index 0000000000..ebf42039b1 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_22.xml @@ -0,0 +1,12 @@ + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_23.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_23.xml new file mode 100644 index 0000000000..cdd3cb1b9f --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_23.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_24.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_24.xml new file mode 100644 index 0000000000..54e0f9a3c0 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_25.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_25.xml new file mode 100644 index 0000000000..0eeb290d9d --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_25.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_26.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_26.xml new file mode 100644 index 0000000000..d863d03c2a --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_26.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_27.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_27.xml new file mode 100644 index 0000000000..a514aeb3d6 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_27.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_28.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_28.xml new file mode 100644 index 0000000000..9ebb3c0904 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_28.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_29.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_29.xml new file mode 100644 index 0000000000..d37bcc33d1 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_29.xml @@ -0,0 +1,9 @@ + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_30.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_30.xml new file mode 100644 index 0000000000..28c2394b99 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_30.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_31.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_31.xml new file mode 100644 index 0000000000..a53cfe99c0 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_31.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_32.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_32.xml new file mode 100644 index 0000000000..15f980bdb1 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_32.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_33.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_33.xml new file mode 100644 index 0000000000..8913d1ffd7 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_33.xml @@ -0,0 +1,12 @@ + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_34.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_34.xml new file mode 100644 index 0000000000..ba3c4313a3 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_34.xml @@ -0,0 +1,9 @@ + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_35.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_35.xml new file mode 100644 index 0000000000..4f7bc1a24f --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_35.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_36.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_36.xml new file mode 100644 index 0000000000..9761204ab6 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_36.xml @@ -0,0 +1,12 @@ + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_37.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_37.xml new file mode 100644 index 0000000000..ac1267cd3b --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_37.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_38.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_38.xml new file mode 100644 index 0000000000..8bb37a35bb --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_38.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_39.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_39.xml new file mode 100644 index 0000000000..48d7150c36 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_39.xml @@ -0,0 +1,27 @@ + + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_40.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_40.xml new file mode 100644 index 0000000000..d18c6e860a --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_40.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_41.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_41.xml new file mode 100644 index 0000000000..18f3149500 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_41.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_42.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_42.xml new file mode 100644 index 0000000000..8e3ecc00c0 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_42.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_43.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_43.xml new file mode 100644 index 0000000000..3b9f51fca5 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_43.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_44.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_44.xml new file mode 100644 index 0000000000..e8f89859d6 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_44.xml @@ -0,0 +1,9 @@ + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_45.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_45.xml new file mode 100644 index 0000000000..98e68c2071 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_45.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_46.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_46.xml new file mode 100644 index 0000000000..de3979434f --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_46.xml @@ -0,0 +1,12 @@ + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_47.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_47.xml new file mode 100644 index 0000000000..4cd1d033f7 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_47.xml @@ -0,0 +1,9 @@ + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_48.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_48.xml new file mode 100644 index 0000000000..7b70654d52 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_48.xml @@ -0,0 +1,12 @@ + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_49.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_49.xml new file mode 100644 index 0000000000..7beda09c4e --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_49.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_50.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_50.xml new file mode 100644 index 0000000000..250388dc4a --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_50.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_51.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_51.xml new file mode 100644 index 0000000000..e317ce1642 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_51.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_52.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_52.xml new file mode 100644 index 0000000000..1427e793c5 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_52.xml @@ -0,0 +1,27 @@ + + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_53.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_53.xml new file mode 100644 index 0000000000..72026cd7a0 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_53.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_54.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_54.xml new file mode 100644 index 0000000000..4097ed9030 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_54.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_55.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_55.xml new file mode 100644 index 0000000000..631da7320d --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_55.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_56.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_56.xml new file mode 100644 index 0000000000..b12c6d245b --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_56.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_57.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_57.xml new file mode 100644 index 0000000000..2622fbe416 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_57.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_58.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_58.xml new file mode 100644 index 0000000000..84f95a8592 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_58.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_59.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_59.xml new file mode 100644 index 0000000000..2f29828bcf --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_59.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_60.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_60.xml new file mode 100644 index 0000000000..b89d033b9e --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_60.xml @@ -0,0 +1,12 @@ + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_61.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_61.xml new file mode 100644 index 0000000000..cbc43e7601 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_61.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_62.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_62.xml new file mode 100644 index 0000000000..9320766492 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_62.xml @@ -0,0 +1,12 @@ + + + + diff --git a/features/verifysession/impl/src/main/res/drawable/ic_verification_63.xml b/features/verifysession/impl/src/main/res/drawable/ic_verification_63.xml new file mode 100644 index 0000000000..f10e4606a9 --- /dev/null +++ b/features/verifysession/impl/src/main/res/drawable/ic_verification_63.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/features/verifysession/impl/src/main/res/values-ar/strings_sas.xml b/features/verifysession/impl/src/main/res/values-ar/strings_sas.xml new file mode 100644 index 0000000000..7bab8eec98 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-ar/strings_sas.xml @@ -0,0 +1,68 @@ + + + + كلب + هِرَّة + أَسَد + حِصَان + حصان وحيد القرن + خِنزِير + فِيل + أَرنَب + باندَا + دِيك + بطريق + سُلحفاة + سَمَكة + أُخطُبُوط + فَرَاشَة + زَهرَة + شَجَرَة + صبار + فُطر + كُرَةٌ أرضِيَّة + قَمَر + سَحابَة + نار + مَوزَة + تُفَّاحَة + فَراوِلَة + ذُرَة + بِيتزا + كَعكَة + قَلب + اِبتِسَامَة + رُوبُوت + قُبَّعَة + نَظَّارَة + مِفتَاحُ رَبط + سانتا + رَفعُ إِبهَام + مِظَلَّة + سَاعَةٌ رَملِيَّة + سَاعَة + هَدِيَّة + مِصبَاح + كِتَاب + قَلَمُ رَصاص + مِشبَكُ وَرَق + مِقَصّ + قُفل + مِفتَاح + مِطرَقَة + تِلِفُون + عَلَم + قِطَار + دَرّاجَة + طَائِرة + صَارُوخ + كَأسُ النَّصر + كُرَة + غيتار + بُوق + جَرَس + مِرسَاة + سَمّاعَة رَأس + مُجَلَّد + دَبُّوس + diff --git a/features/verifysession/impl/src/main/res/values-bg/strings_sas.xml b/features/verifysession/impl/src/main/res/values-bg/strings_sas.xml new file mode 100644 index 0000000000..cae262847f --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-bg/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Куче + Котка + Лъв + Кон + Еднорог + Прасе + Слон + Заек + Панда + Петел + Пингвин + Костенурка + Риба + Октопод + Пеперуда + Цвете + Дърво + Кактус + Гъба + Глобус + Луна + Облак + Огън + Банан + Ябълка + Ягода + Царевица + Пица + Торта + Сърце + Усмивка + Робот + Шапка + Очила + Гаечен ключ + Дядо Коледа + Палец нагоре + Чадър + Пясъчен часовник + Часовник + Подарък + Лампа + Книга + Молив + Кламер + Ножици + Катинар + Ключ + Чук + Телефон + Флаг + Влак + Колело + Самолет + Ракета + Трофей + Топка + Китара + Тромпет + Звънец + Котва + Слушалки + Папка + Кабърче + diff --git a/features/verifysession/impl/src/main/res/values-ca/strings_sas.xml b/features/verifysession/impl/src/main/res/values-ca/strings_sas.xml new file mode 100644 index 0000000000..07201480a8 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-ca/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Gos + Gat + Lleó + Cavall + Unicorn + Porc + Elefant + Conill + Panda + Gall + Pingüí + Tortuga + Peix + Pop + Papallona + Flor + Arbre + Cactus + Bolet + Globus terraqüi + Lluna + Núvol + Foc + Plàtan + Poma + Maduixa + Blat de moro + Pizza + Pastís + Cor + Somrient + Robot + Barret + Ulleres + Clau anglesa + Pare Noél + Polzes amunt + Paraigües + Rellotge de sorra + Rellotge + Regal + Bombeta + Llibre + Llapis + Clip + Tisores + Cadenat + Clau + Martell + Telèfon + Bandera + Tren + Bicicleta + Avió + Coet + Trofeu + Pilota + Guitarra + Trompeta + Campana + Àncora + Auriculars + Carpeta + Xinxeta + diff --git a/features/verifysession/impl/src/main/res/values-cs/strings_sas.xml b/features/verifysession/impl/src/main/res/values-cs/strings_sas.xml new file mode 100644 index 0000000000..6f0eaa0703 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-cs/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Pes + Kočka + Lev + Kůň + Jednorožec + Prase + Slon + Králík + Panda + Kohout + Tučňák + Želva + Ryba + Chobotnice + Motýl + Květina + Strom + Kaktus + Houba + Zeměkoule + Měsíc + Mrak + Oheň + Banán + Jablko + Jahoda + Kukuřice + Pizza + Dort + Srdce + Smajlík + Robot + Klobouk + Brýle + Klíč + Mikuláš + Palec nahoru + Deštník + Přesýpací hodiny + Hodiny + Dárek + Žárovka + Kniha + Tužka + Sponka + Nůžky + Zámek + Klíč ke dveřím + Kladivo + Telefon + Vlajka + Vlak + Kolo + Letadlo + Raketa + Pohár + Míč + Kytara + Trumpeta + Zvonek + Kotva + Sluchátka + Složka + Špendlík + diff --git a/features/verifysession/impl/src/main/res/values-cs/translations.xml b/features/verifysession/impl/src/main/res/values-cs/translations.xml index 60e9cf17ce..625839877a 100644 --- a/features/verifysession/impl/src/main/res/values-cs/translations.xml +++ b/features/verifysession/impl/src/main/res/values-cs/translations.xml @@ -3,6 +3,8 @@ "Něco není v pořádku. Buď vypršel časový limit požadavku, nebo byl požadavek zamítnut." "Zkontrolujte, zda se níže uvedené emotikony shodují s emotikony zobrazenými na jiné relaci." "Porovnání emotikonů" + "Potvrďte, že níže uvedená čísla odpovídají číslům zobrazeným na vaší druhé relaci." + "Porovnejte čísla" "Vaše nová relace je nyní ověřena. Má přístup k vašim zašifrovaným zprávám a ostatní uživatelé ji uvidí jako důvěryhodnou." "Pro přístup k historii zašifrovaných zpráv prokažte, že jste to vy." "Otevřete existující relaci" diff --git a/features/verifysession/impl/src/main/res/values-de/strings_sas.xml b/features/verifysession/impl/src/main/res/values-de/strings_sas.xml new file mode 100644 index 0000000000..fc81895816 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-de/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Hund + Katze + Löwe + Pferd + Einhorn + Schwein + Elefant + Hase + Panda + Hahn + Pinguin + Schildkröte + Fisch + Oktopus + Schmetterling + Blume + Baum + Kaktus + Pilz + Globus + Mond + Wolke + Feuer + Banane + Apfel + Erdbeere + Mais + Pizza + Kuchen + Herz + Lächeln + Roboter + Hut + Brille + Schraubenschlüssel + Weihnachtsmann + Daumen Hoch + Regenschirm + Sanduhr + Uhr + Geschenk + Glühbirne + Buch + Bleistift + Büroklammer + Schere + Schloss + Schlüssel + Hammer + Telefon + Flagge + Zug + Fahrrad + Flugzeug + Rakete + Pokal + Ball + Gitarre + Trompete + Glocke + Anker + Kopfhörer + Ordner + Stecknadel + diff --git a/features/verifysession/impl/src/main/res/values-de/translations.xml b/features/verifysession/impl/src/main/res/values-de/translations.xml index 7ac1ddce6f..6fac01495c 100644 --- a/features/verifysession/impl/src/main/res/values-de/translations.xml +++ b/features/verifysession/impl/src/main/res/values-de/translations.xml @@ -3,12 +3,15 @@ "Etwas scheint nicht zu stimmen. Entweder ist das Zeitlimit für die Anfrage abgelaufen oder die Anfrage wurde abgelehnt." "Vergewissere dich dass die folgenden Emojis mit denen in deiner anderen Session übereinstimmen." "Emojis vergleichen" + "Bestätige, dass die Zahlen mit denen deiner anderen Sitzung übereinstimmen." + "Vergleiche die Zahlen" "Deine neue Session ist nun verifiziert. Sie hat Zugriff auf deine verschlüsselten Nachrichten und wird von anderen Benutzern als vertrauenswürdig eingestuft." "Beweise deine Identität, um auf deinen verschlüsselten Nachrichtenverlauf zuzugreifen." "Öffne eine bestehende Session" "Verifizierung wiederholen" "Ich bin bereit" "Warten auf eine Übereinstimmung" + "Vergleiche eine spezielle Reihe von Emojis." "Vergleiche die einzelnen Emojis und stelle sicher, dass sie in der gleichen Reihenfolge erscheinen." "Sie stimmen nicht überein" "Sie stimmen überein" diff --git a/features/verifysession/impl/src/main/res/values-eo/strings_sas.xml b/features/verifysession/impl/src/main/res/values-eo/strings_sas.xml new file mode 100644 index 0000000000..d9efa50fb7 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-eo/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Hundo + Kato + Leono + Ĉevalo + Unukorno + Porko + Elefanto + Kuniklo + Pando + Virkoko + Pingveno + Testudo + Fiŝo + Polpo + Papilio + Floro + Arbo + Kakto + Fungo + Globo + Luno + Nubo + Fajro + Banano + Pomo + Frago + Maizo + Pico + Torto + Koro + Rideto + Roboto + Ĉapelo + Okulvitroj + Ŝraŭbŝlosilo + Kristnaska viro + Dikfingro supren + Ombrelo + Sablohorloĝo + Horloĝo + Donaco + Lampo + Libro + Krajono + Paperkuntenilo + Tondilo + Seruro + Ŝlosilo + Martelo + Telefono + Flago + Vagonaro + Biciklo + Aviadilo + Raketo + Trofeo + Pilko + Gitaro + Trumpeto + Sonorilo + Ankro + Kapaŭdilo + Dosierujo + Pinglo + diff --git a/features/verifysession/impl/src/main/res/values-es/strings_sas.xml b/features/verifysession/impl/src/main/res/values-es/strings_sas.xml new file mode 100644 index 0000000000..cc78a46ec3 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-es/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Perro + Gato + León + Caballo + Unicornio + Cerdo + Elefante + Conejo + Panda + Gallo + Pingüino + Tortuga + Pez + Pulpo + Mariposa + Flor + Árbol + Cactus + Seta + Globo + Luna + Nube + Fuego + Plátano + Manzana + Fresa + Maíz + Pizza + Tarta + Corazón + Emoticono + Robot + Sombrero + Gafas + Llave inglesa + Papá Noel + Pulgar arriba + Paraguas + Reloj de arena + Reloj + Regalo + Bombilla + Libro + Lápiz + Clip + Tijeras + Candado + Llave + Martillo + Teléfono + Bandera + Tren + Bicicleta + Avión + Cohete + Trofeo + Bola + Guitarra + Trompeta + Campana + Ancla + Cascos + Carpeta + Alfiler + diff --git a/features/verifysession/impl/src/main/res/values-et/strings_sas.xml b/features/verifysession/impl/src/main/res/values-et/strings_sas.xml new file mode 100644 index 0000000000..1223b67104 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-et/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Koer + Kass + Lõvi + Hobune + Ükssarvik + Siga + Elevant + Jänes + Panda + Kukk + Pingviin + Kilpkonn + Kala + Kaheksajalg + Liblikas + Lill + Puu + Kaktus + Seen + Maakera + Kuu + Pilv + Tuli + Banaan + Õun + Maasikas + Mais + Pitsa + Kook + Süda + Smaili + Robot + Kübar + Prillid + Mutrivõti + Jõuluvana + Pöidlad püsti + Vihmavari + Liivakell + Kell + Kingitus + Lambipirn + Raamat + Pliiats + Kirjaklamber + Käärid + Lukk + Võti + Haamer + Telefon + Lipp + Rong + Jalgratas + Lennuk + Rakett + Auhind + Pall + Kitarr + Trompet + Kelluke + Ankur + Kõrvaklapid + Kaust + Nööpnõel + diff --git a/features/verifysession/impl/src/main/res/values-fa/strings_sas.xml b/features/verifysession/impl/src/main/res/values-fa/strings_sas.xml new file mode 100644 index 0000000000..42efb247bb --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-fa/strings_sas.xml @@ -0,0 +1,68 @@ + + + + سگ + گربه + شیر + اسب + تک شاخ + خوک + فیل + خرگوش + پاندا + خروس + پنگوئن + لاک‌پشت + ماهی + اختاپوس + پروانه + گل + درخت + کاکتوس + قارچ + زمین + ماه + ابر + آتش + موز + سیب + توت فرنگی + ذرت + پیتزا + کیک + قلب + خنده + ربات + کلاه + عینک + آچار + بابا نوئل + لایک + چتر + ساعت شنی + ساعت + هدیه + لامپ + کتاب + مداد + گیره کاغذ + قیچی + قفل + کلید + چکش + تلفن + پرچم + قطار + دوچرخه + هواپیما + موشک + جام + توپ + گیتار + شیپور + زنگ + لنگر + هدفون + پوشه + سنجاق + diff --git a/features/verifysession/impl/src/main/res/values-fi/strings_sas.xml b/features/verifysession/impl/src/main/res/values-fi/strings_sas.xml new file mode 100644 index 0000000000..c51447706e --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-fi/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Koira + Kissa + Leijona + Hevonen + Yksisarvinen + Sika + Norsu + Kani + Panda + Kukko + Pingviini + Kilpikonna + Kala + Tursas + Perhonen + Kukka + Puu + Kaktus + Sieni + Maapallo + Kuu + Pilvi + Tuli + Banaani + Omena + Mansikka + Maissi + Pizza + Kakku + Sydän + Hymynaama + Robotti + Hattu + Silmälasit + Kiintoavain + Joulupukki + Peukalo ylös + Sateenvarjo + Tiimalasi + Pöytäkello + Lahja + Hehkulamppu + Kirja + Lyijykynä + Paperiliitin + Sakset + Lukko + Avain + Vasara + Puhelin + Lippu + Juna + Polkupyörä + Lentokone + Raketti + Palkinto + Pallo + Kitara + Trumpetti + Soittokello + Ankkuri + Kuulokkeet + Kansio + Nuppineula + diff --git a/features/verifysession/impl/src/main/res/values-fr/strings_sas.xml b/features/verifysession/impl/src/main/res/values-fr/strings_sas.xml new file mode 100644 index 0000000000..3edd4b26fb --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-fr/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Chien + Chat + Lion + Cheval + Licorne + Cochon + Éléphant + Lapin + Panda + Coq + Manchot + Tortue + Poisson + Poulpe + Papillon + Fleur + Arbre + Cactus + Champignon + Globe + Lune + Nuage + Feu + Banane + Pomme + Fraise + Maïs + Pizza + Gâteau + Cœur + Sourire + Robot + Chapeau + Lunettes + Clé à molette + Père Noël + Pouce en l’air + Parapluie + Sablier + Réveil + Cadeau + Ampoule + Livre + Crayon + Trombone + Ciseaux + Cadenas + Clé + Marteau + Téléphone + Drapeau + Train + Vélo + Avion + Fusée + Trophée + Ballon + Guitare + Trompette + Cloche + Ancre + Casque audio + Dossier + Punaise + diff --git a/features/verifysession/impl/src/main/res/values-fr/translations.xml b/features/verifysession/impl/src/main/res/values-fr/translations.xml index c9d3446f2a..8473d7d6bd 100644 --- a/features/verifysession/impl/src/main/res/values-fr/translations.xml +++ b/features/verifysession/impl/src/main/res/values-fr/translations.xml @@ -3,12 +3,15 @@ "Quelque chose ne va pas. Soit la demande a expiré, soit elle a été refusée." "Confirmez que les emojis ci-dessous correspondent à ceux affichés sur votre autre session." "Comparez les émojis" + "Confirmez que les nombres ci-dessous correspondent à ceux affichés sur votre autre session." + "Comparez les nombres" "Votre nouvelle session est désormais vérifiée. Elle a accès à vos messages chiffrés et les autres utilisateurs la verront identifiée comme fiable." "Prouvez qu’il s’agit bien de vous pour accéder à l’historique de vos messages chiffrés." "Ouvrir une session existante" "Réessayer la vérification" "Je suis prêt.e" "En attente de correspondance" + "Comparer un groupe unique d’Emojis." "Comparez les emoji uniques en veillant à ce qu’ils apparaissent dans le même ordre." "Ils ne correspondent pas" "Ils correspondent" diff --git a/features/verifysession/impl/src/main/res/values-hr/strings_sas.xml b/features/verifysession/impl/src/main/res/values-hr/strings_sas.xml new file mode 100644 index 0000000000..22f3e62ae2 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-hr/strings_sas.xml @@ -0,0 +1,68 @@ + + + + pas + mačka + lav + konj + jednorog + svinja + slon + zec + panda + kokot + pingvin + kornjača + riba + hobotnica + leptir + svijet + drvo + kaktus + gljiva + Globus + mjesec + oblak + vatra + banana + jabuka + jagoda + kukuruza + pizza + torta + srca + smajlića + robot + kapa + naočale + ključ + deda Mraz + palac gore + kišobran + pješčani sat + sat + poklon + žarulja + knjiga + olovka + spajalica + škare + zaključati + ključ + čekić + telefon + zastava + vlak + bicikl + avion + raketa + trofej + lopta + gitara + truba + zvono + sidro + slušalice + mapu + pribadača + diff --git a/features/verifysession/impl/src/main/res/values-hu/strings_sas.xml b/features/verifysession/impl/src/main/res/values-hu/strings_sas.xml new file mode 100644 index 0000000000..8137042078 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-hu/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Kutya + Macska + Oroszlán + + Egyszarvú + Malac + Elefánt + Nyúl + Panda + Kakas + Pingvin + Teknős + Hal + Polip + Pillangó + Virág + Fa + Kaktusz + Gomba + Földgömb + Hold + Felhő + Tűz + Banán + Alma + Eper + Kukorica + Pizza + Süti + Szív + Mosoly + Robot + Kalap + Szemüveg + Csavarkulcs + Télapó + Hüvelykujj fel + Esernyő + Homokóra + Óra + Ajándék + Égő + Könyv + Ceruza + Gémkapocs + Olló + Lakat + Kulcs + Kalapács + Telefon + Zászló + Vonat + Kerékpár + Repülő + Rakáta + Trófea + Labda + Gitár + Trombita + Harang + Horgony + Fejhallgató + Mappa + Rajszeg + diff --git a/features/verifysession/impl/src/main/res/values-hu/translations.xml b/features/verifysession/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..97092394b8 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,20 @@ + + + "Valami hibásnak tűnik. A kérés vagy időtúllépésre futott, vagy elutasították." + "Erősítse meg, hogy a lenti emodzsik egyeznek-e a másik munkamenetben megjelenítettekkel." + "Emodzsik összehasonlítása" + "Ellenőrizze, hogy az alábbi számok megegyeznek-e a másik munkamenetben feltüntetett számokkal." + "Számok összehasonlítása" + "Az új munkamenete most már ellenőrizve van. Eléri a titkosított üzeneteit, és a többi felhasználó is megbízhatónak fogja látni." + "Bizonyítsa, hogy valóban Ön az, hogy elérje a titkosított üzeneteinek előzményeit." + "Meglévő munkamenet megnyitása" + "Ellenőrzés újrapróbálása" + "Kész vagyok" + "Várakozás az egyezésre" + "Egyedi emodzsik összehasonlítása." + "Hasonlítsa össze az egyedi emodzsikat, meggyőződve arról, hogy azonos a sorrendjük." + "Nem egyeznek" + "Megegyeznek" + "A folytatáshoz fogadja el az ellenőrzési folyamat indítási kérését a másik munkamenetében." + "Várakozás a kérés elfogadására" + diff --git a/features/verifysession/impl/src/main/res/values-in/strings_sas.xml b/features/verifysession/impl/src/main/res/values-in/strings_sas.xml new file mode 100644 index 0000000000..1b283a7438 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-in/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Anjing + Kucing + Singa + Kuda + Unicorn + Babi + Gajah + Kelinci + Panda + Ayam + Penguin + Kura-Kura + Ikan + Gurita + Kupu-Kupu + Bunga + Pohon + Kaktus + Jamur + Bola Dunia + Bulan + Awan + Api + Pisang + Apel + Stroberi + Jagung + Pizza + Kue + Hati + Senyuman + Robot + Topi + Kacamata + Kunci Bengkel + Santa + Jempol + Payung + Jam Pasir + Jam + Kado + Bohlam Lampu + Buku + Pensil + Klip Kertas + Gunting + Gembok + Kunci + Palu + Telepon + Bendera + Kereta Api + Sepeda + Pesawat + Roket + Piala + Bola + Gitar + Terompet + Lonceng + Jangkar + Headphone + Map + Pin + diff --git a/features/verifysession/impl/src/main/res/values-in/translations.xml b/features/verifysession/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..0c0a7dad4d --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,18 @@ + + + "Sepertinya ada yang tidak beres. Entah permintaan sudah habis masa berlakunya atau permintaan ditolak." + "Konfirmasikan bahwa emoji di bawah ini sesuai dengan yang ditampilkan pada sesi Anda yang lain." + "Bandingkan emoji" + "Sesi baru Anda sekarang diverifikasi. Ini memiliki akses ke pesan terenkripsi Anda, dan pengguna lain akan melihatnya sebagai tepercaya." + "Buktikan bahwa ini memang Anda untuk mengakses riwayat pesan terenkripsi Anda." + "Buka sesi yang sudah ada" + "Verifikasi ulang" + "Saya siap" + "Menunggu untuk mencocokkan" + "Bandingkan satu set emoji yang unik." + "Bandingkan emoji unik, dan pastikan emoji tersebut muncul dalam urutan yang sama." + "Mereka tidak cocok" + "Mereka cocok" + "Terima permintaan untuk memulai proses verifikasi di sesi Anda yang lain untuk melanjutkan." + "Menunggu untuk menerima permintaan" + diff --git a/features/verifysession/impl/src/main/res/values-it/strings_sas.xml b/features/verifysession/impl/src/main/res/values-it/strings_sas.xml new file mode 100644 index 0000000000..d0dca4b835 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-it/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Cane + Gatto + Leone + Cavallo + Unicorno + Maiale + Elefante + Coniglio + Panda + Gallo + Pinguino + Tartaruga + Pesce + Polpo + Farfalla + Fiore + Albero + Cactus + Fungo + Globo + Luna + Nuvola + Fuoco + Banana + Mela + Fragola + Mais + Pizza + Torta + Cuore + Faccina sorridente + Robot + Cappello + Occhiali + Chiave inglese + Babbo Natale + Pollice alzato + Ombrello + Clessidra + Orologio + Regalo + Lampadina + Libro + Matita + Graffetta + Forbici + Lucchetto + Chiave + Martello + Telefono + Bandiera + Treno + Bicicletta + Aeroplano + Razzo + Trofeo + Palla + Chitarra + Trombetta + Campana + Ancora + Cuffie + Cartella + Puntina + diff --git a/features/verifysession/impl/src/main/res/values-ja/strings_sas.xml b/features/verifysession/impl/src/main/res/values-ja/strings_sas.xml new file mode 100644 index 0000000000..9791b688fe --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-ja/strings_sas.xml @@ -0,0 +1,68 @@ + + + + + + ライオン + + ユニコーン + ブタ + ゾウ + うさぎ + パンダ + ニワトリ + ペンギン + + + たこ + ちょうちょ + + + サボテン + きのこ + 地球 + + + + バナナ + リンゴ + いちご + とうもろこし + ピザ + ケーキ + ハート + スマイル + ロボット + 帽子 + めがね + スパナ + サンタ + いいね + + 砂時計 + 時計 + ギフト + 電球 + + 鉛筆 + クリップ + はさみ + 錠前 + + 金槌 + 電話機 + + 電車 + 自転車 + 飛行機 + ロケット + トロフィー + ボール + ギター + トランペット + ベル + いかり + ヘッドホン + フォルダー + ピン + diff --git a/features/verifysession/impl/src/main/res/values-nb-rNO/strings_sas.xml b/features/verifysession/impl/src/main/res/values-nb-rNO/strings_sas.xml new file mode 100644 index 0000000000..48c830a22e --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-nb-rNO/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Hund + Katt + Løve + Hest + Enhjørning + Gris + Elefant + Kanin + Panda + Hane + Pingvin + Skilpadde + Fisk + Blekksprut + Sommerfugl + Blomst + Tre + Kaktus + Sopp + Globus + Måne + Sky + Flamme + Banan + Eple + Jordbær + Mais + Pizza + Kake + Hjerte + Smilefjes + Robot + Hatt + Briller + Fastnøkkel + Julenisse + Tommel Opp + Paraply + Timeglass + Klokke + Gave + Lyspære + Bok + Blyant + BInders + Saks + Lås + Nøkkel + Hammer + Telefon + Flagg + Tog + Sykkel + Fly + Rakett + Pokal + Ball + Gitar + Trompet + Bjelle + Anker + Hodetelefoner + Mappe + Tegnestift + diff --git a/features/verifysession/impl/src/main/res/values-nl/strings_sas.xml b/features/verifysession/impl/src/main/res/values-nl/strings_sas.xml new file mode 100644 index 0000000000..24a889f58a --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-nl/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Hond + Kat + Leeuw + Paard + Eenhoorn + Varken + Olifant + Konijn + Panda + Haan + Pinguïn + Schildpad + Vis + Octopus + Vlinder + Bloem + Boom + Cactus + Paddenstoel + Wereldbol + Maan + Wolk + Vuur + Banaan + Appel + Aardbei + Maïs + Pizza + Taart + Hart + Smiley + Robot + Hoed + Bril + Moersleutel + Kerstman + Duim omhoog + Paraplu + Zandloper + Wekker + Geschenk + Gloeilamp + Boek + Potlood + Papierklemmetje + Schaar + Slot + Sleutel + Hamer + Telefoon + Vlag + Trein + Fiets + Vliegtuig + Raket + Trofee + Bal + Gitaar + Trompet + Bel + Anker + Koptelefoon + Map + Duimspijker + diff --git a/features/verifysession/impl/src/main/res/values-pt-rBR/strings_sas.xml b/features/verifysession/impl/src/main/res/values-pt-rBR/strings_sas.xml new file mode 100644 index 0000000000..34add983e2 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-pt-rBR/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Cachorro + Gato + Leão + Cavalo + Unicórnio + Porco + Elefante + Coelho + Panda + Galo + Pinguim + Tartaruga + Peixe + Polvo + Borboleta + Flor + Árvore + Cacto + Cogumelo + Globo + Lua + Nuvem + Fogo + Banana + Maçã + Morango + Milho + Pizza + Bolo + Coração + Sorriso + Robô + Chapéu + Óculos + Chave inglesa + Papai-noel + Joinha + Guarda-chuva + Ampulheta + Relógio + Presente + Lâmpada + Livro + Lápis + Clipe de papel + Tesoura + Cadeado + Chave + Martelo + Telefone + Bandeira + Trem + Bicicleta + Avião + Foguete + Troféu + Bola + Guitarra + Trombeta + Sino + Âncora + Fones de ouvido + Pasta + Alfinete + diff --git a/features/verifysession/impl/src/main/res/values-pt/strings_sas.xml b/features/verifysession/impl/src/main/res/values-pt/strings_sas.xml new file mode 100644 index 0000000000..c422b2a9c7 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-pt/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Cão + Gato + Leão + Cavalo + Unicórnio + Porco + Elefante + Coelho + Panda + Galo + Pinguim + Tartaruga + Peixe + Polvo + Borboleta + Flor + Árvore + Cato + Cogumelo + Globo + Lua + Nuvem + Fogo + Banana + Maçã + Morango + Milho + Piza + Bolo + Coração + Sorriso + Robô + Chapéu + Óculos + Chave inglesa + Pai Natal + Polegar para cima + Guarda-chuva + Ampulheta + Relógio + Presente + Lâmpada + Livro + Lápis + Clipe + Tesoura + Cadeado + Chave + Martelo + Telefone + Bandeira + Comboio + Bicicleta + Avião + Foguetão + Troféu + Bola + Guitarra + Trompete + Sino + Âncora + Fones + Pasta + Pionés + diff --git a/features/verifysession/impl/src/main/res/values-ru/strings_sas.xml b/features/verifysession/impl/src/main/res/values-ru/strings_sas.xml new file mode 100644 index 0000000000..9a26caf62e --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-ru/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Собака + Кошка + Лев + Лошадь + Единорог + Свинья + Слон + Кролик + Панда + Петух + Пингвин + Черепаха + Рыба + Осьминог + Бабочка + Цветок + Дерево + Кактус + Гриб + Глобус + Луна + Облако + Огонь + Банан + Яблоко + Клубника + Кукуруза + Пицца + Торт + Сердце + Улыбка + Робот + Шляпа + Очки + Ключ + Санта + Большой палец вверх + Зонт + Песочные часы + Часы + Подарок + Лампочка + Книга + Карандаш + Скрепка + Ножницы + Замок + Ключ + Молоток + Телефон + Флаг + Поезд + Велосипед + Самолет + Ракета + Кубок + Мяч + Гитара + Труба + Колокол + Якорь + Наушники + Папка + Булавка + diff --git a/features/verifysession/impl/src/main/res/values-ru/translations.xml b/features/verifysession/impl/src/main/res/values-ru/translations.xml index 564f839591..8351059b5d 100644 --- a/features/verifysession/impl/src/main/res/values-ru/translations.xml +++ b/features/verifysession/impl/src/main/res/values-ru/translations.xml @@ -3,6 +3,8 @@ "Кажется, что-то не так. Время ожидания запроса истекло, либо запрос был отклонен." "Убедитесь, что приведенные ниже смайлики совпадают со смайликами, показанными во время другого сеанса." "Сравните емодзи" + "Убедитесь, что приведенные ниже числа совпадают с цифрами, показанными в другом сеансе." + "Сравните числа" "Ваш новый сеанс подтвержден. У него есть доступ к вашим зашифрованным сообщениям, и другие пользователи увидят его как доверенное." "Чтобы получить доступ к зашифрованной истории сообщений, докажите, что это вы." "Открыть существующий сеанс" diff --git a/features/verifysession/impl/src/main/res/values-si/strings_sas.xml b/features/verifysession/impl/src/main/res/values-si/strings_sas.xml new file mode 100644 index 0000000000..67d0775281 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-si/strings_sas.xml @@ -0,0 +1,8 @@ + + + + බල්ලා + පූසා + සිංහයා + අශ්වයා + diff --git a/features/verifysession/impl/src/main/res/values-sk/strings_sas.xml b/features/verifysession/impl/src/main/res/values-sk/strings_sas.xml new file mode 100644 index 0000000000..5e1eba9569 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-sk/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Pes + Mačka + Lev + Kôň + Jednorožec + Prasa + Slon + Zajac + Panda + Kohút + Tučniak + Korytnačka + Ryba + Chobotnica + Motýľ + Kvet + Strom + Kaktus + Huba + Zemeguľa + Mesiac + Oblak + Oheň + Banán + Jablko + Jahoda + Kukurica + Pizza + Torta + Srdce + Smajlík + Robot + Klobúk + Okuliare + Vidlicový kľúč + Mikuláš + Palec nahor + Dáždnik + Presýpacie hodiny + Budík + Darček + Žiarovka + Kniha + Ceruzka + Kancelárska sponka + Nožnice + Zámka + Kľúč + Kladivo + Telefón + Zástava + Vlak + Bicykel + Lietadlo + Raketa + Trofej + Lopta + Gitara + Trúbka + Zvonec + Kotva + Slúchadlá + Fascikel + Špendlík + diff --git a/features/verifysession/impl/src/main/res/values-sk/translations.xml b/features/verifysession/impl/src/main/res/values-sk/translations.xml index 4b13701466..5d7920d740 100644 --- a/features/verifysession/impl/src/main/res/values-sk/translations.xml +++ b/features/verifysession/impl/src/main/res/values-sk/translations.xml @@ -3,6 +3,8 @@ "Zdá sa, že niečo nie je v poriadku. Časový limit žiadosti vypršal alebo bola žiadosť zamietnutá." "Skontrolujte, či sa emotikony uvedené nižšie zhodujú s emotikonmi zobrazenými vo vašej druhej relácii." "Porovnajte emotikony" + "Skontrolujte, či sa nižšie uvedené čísla zhodujú s číslami zobrazenými na vašej druhej relácii." + "Porovnať čísla" "Vaša nová relácia je teraz overená. Má prístup k vašim zašifrovaným správam a ostatní používatelia ju budú vidieť ako dôveryhodnú." "Dokážte, že ste to vy, aby ste získali prístup k histórii vašich zašifrovaných správ." "Otvoriť existujúcu reláciu" diff --git a/features/verifysession/impl/src/main/res/values-sq/strings_sas.xml b/features/verifysession/impl/src/main/res/values-sq/strings_sas.xml new file mode 100644 index 0000000000..b305c9788f --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-sq/strings_sas.xml @@ -0,0 +1,67 @@ + + + + Qen + Mace + Luan + Kalë + Njëbrirësh + Derr + Elefant + Lepur + Panda + Këndes + Pinguin + Breshkë + Peshk + Oktapod + Flutur + Lule + Pemë + Kaktus + Kërpudhë + Rruzull + Hënë + Re + Zjarr + Banane + Mollë + Luleshtrydhe + Misër + Picë + Tortë + Zemër + Emotikon + Robot + Kapë + Syze + Çelës + Babagjyshi i Vitit të Ri + Ombrellë + Klepsidër + Sahat + Dhuratë + Llambë + Libër + Laps + Kapëse + Gërshërë + Dry + Çelës + Çekiç + Telefon + Flamur + Tren + Biçikletë + Avion + Raketë + Trofe + Top + Kitarë + Trombë + Kambanë + Spirancë + Kufje + Dosje + Karficë + diff --git a/features/verifysession/impl/src/main/res/values-sr/strings_sas.xml b/features/verifysession/impl/src/main/res/values-sr/strings_sas.xml new file mode 100644 index 0000000000..f9e1c9d201 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-sr/strings_sas.xml @@ -0,0 +1,68 @@ + + + + пас + мачка + лав + коњ + једнорог + прасе + слон + зец + панда + петао + пингвин + корњача + риба + октопод + лептир + цвет + дрво + кактус + печурка + глобус + месец + облак + ватра + банана + јабука + јагода + кукуруз + пица + торта + срце + смајли + робот + шешир + наочаре + кључ + деда Мраз + палчић горе + кишобран + пешчаник + сат + поклон + сијалица + књига + оловка + спајалица + маказе + катанац + кључ + чекић + телефон + застава + воз + бицикл + авион + ракета + пехар + лопта + гитара + труба + звоно + сидро + слушалице + фасцикла + чиода + diff --git a/features/verifysession/impl/src/main/res/values-sv/strings_sas.xml b/features/verifysession/impl/src/main/res/values-sv/strings_sas.xml new file mode 100644 index 0000000000..02cf63c37f --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-sv/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Hund + Katt + Lejon + Häst + Enhörning + Gris + Elefant + Kanin + Panda + Tupp + Pingvin + Sköldpadda + Fisk + Bläckfisk + Fjäril + Blomma + Träd + Kaktus + Svamp + Jordklot + Måne + Moln + Eld + Banan + Äpple + Jordgubbe + Majs + Pizza + Tårta + Hjärta + Smiley + Robot + Hatt + Glasögon + Skruvnyckel + Tomte + Tummen upp + Paraply + Timglas + Klocka + Present + Lampa + Bok + Penna + Gem + Sax + Lås + Nyckel + Hammare + Telefon + Flagga + Tåg + Cykel + Flygplan + Raket + Trofé + Boll + Gitarr + Trumpet + Bjällra + Ankare + Hörlurar + Mapp + Häftstift + diff --git a/features/verifysession/impl/src/main/res/values-szl/strings_sas.xml b/features/verifysession/impl/src/main/res/values-szl/strings_sas.xml new file mode 100644 index 0000000000..9769ad73ce --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-szl/strings_sas.xml @@ -0,0 +1,4 @@ + + + + diff --git a/features/verifysession/impl/src/main/res/values-tzm/strings_sas.xml b/features/verifysession/impl/src/main/res/values-tzm/strings_sas.xml new file mode 100644 index 0000000000..6d1567d6dd --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-tzm/strings_sas.xml @@ -0,0 +1,30 @@ + + + + Aydi + Amuc + Izem + Ayyis + Ilef + Ilu + Agnin + Ayaẓiḍ + Ifker + Aselm + Aseklu + Agursel + Ayyur + Timessi + Tabanant + Tadeffuyt + Ul + Aṛubu + Taraza + Adlis + Tasarut + Atilifun + Acenyal + Tcama + Agiṭaṛ + Asdaw + diff --git a/features/verifysession/impl/src/main/res/values-uk/strings_sas.xml b/features/verifysession/impl/src/main/res/values-uk/strings_sas.xml new file mode 100644 index 0000000000..bfeaa3a6d1 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-uk/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Пес + Кіт + Лев + Кінь + Єдиноріг + Свиня + Слон + Кріль + Панда + Когут + Пінгвін + Черепаха + Риба + Восьминіг + Метелик + Квітка + Дерево + Кактус + Гриб + Глобус + Місяць + Хмара + Вогонь + Банан + Яблуко + Полуниця + Кукурудза + Піца + Пиріг + Серце + Посмішка + Робот + Капелюх + Окуляри + Гайковий ключ + Санта Клаус + Великий палець вгору + Парасолька + Пісковий годинник + Годинник + Подарунок + Лампочка + Книга + Олівець + Спиначка + Ножиці + Замок + Ключ + Молоток + Телефон + Прапор + Потяг + Велосипед + Літак + Ракета + Приз + М\'яч + Гітара + Труба + Дзвін + Якір + Навушники + Тека + Кнопка + diff --git a/features/verifysession/impl/src/main/res/values-vi/strings_sas.xml b/features/verifysession/impl/src/main/res/values-vi/strings_sas.xml new file mode 100644 index 0000000000..dbf7db0234 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-vi/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Chó + Mèo + Sư tử + Ngựa + Kỳ lân + Heo + Voi + Thỏ + Gấu trúc + Gà trống + Chim cánh cụt + Rùa + + Bạch tuộc + Bướm + Hoa + Cây + Xương rồng + Nấm + Địa cầu + Mặt trăng + Mây + Lửa + Chuối + Táo + Dâu tây + Bắp + Pizza + Bánh + Tim + Mặt cười + Rô-bô + + Kính mắt + Cờ-lê + ông già Nô-en + Thích + Cái ô + Đồng hồ cát + Đồng hồ + Quà tặng + Bóng đèn tròn + Sách + Viết chì + Kẹp giấy + Cái kéo + Ổ khóa + Chìa khóa + Búa + Điện thoại + Lá cờ + Xe lửa + Xe đạp + Máy bay + Tên lửa + Cúp + Banh + Ghi-ta + Kèn + Chuông + Mỏ neo + Tai nghe + Thư mục + Ghim + diff --git a/features/verifysession/impl/src/main/res/values-zh-rCN/strings_sas.xml b/features/verifysession/impl/src/main/res/values-zh-rCN/strings_sas.xml new file mode 100644 index 0000000000..39c2ff1d37 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-zh-rCN/strings_sas.xml @@ -0,0 +1,68 @@ + + + + + + 狮子 + + 独角兽 + + 大象 + 兔子 + 熊猫 + 公鸡 + 企鹅 + 乌龟 + + 章鱼 + 蝴蝶 + + + 仙人掌 + 蘑菇 + 地球 + 月亮 + + + 香蕉 + 苹果 + 草莓 + 玉米 + 披萨 + 蛋糕 + + 笑脸 + 机器人 + 帽子 + 眼镜 + 扳手 + 圣诞老人 + + + 沙漏 + 时钟 + 礼物 + 灯泡 + + 铅笔 + 回形针 + 剪刀 + + 钥匙 + 锤子 + 电话 + 旗帜 + 火车 + 自行车 + 飞机 + 火箭 + 奖杯 + + 吉他 + 喇叭 + 铃铛 + + 耳机 + 文件夹 + 图钉 + diff --git a/features/verifysession/impl/src/main/res/values-zh-rTW/strings_sas.xml b/features/verifysession/impl/src/main/res/values-zh-rTW/strings_sas.xml new file mode 100644 index 0000000000..b7068b7f67 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-zh-rTW/strings_sas.xml @@ -0,0 +1,68 @@ + + + + + + 獅子 + + 獨角獸 + + 大象 + 兔子 + 熊貓 + 公雞 + 企鵝 + 烏龜 + + 章魚 + 蝴蝶 + + + 仙人掌 + 蘑菇 + 地球 + 月亮 + 雲朵 + + 香蕉 + 蘋果 + 草莓 + 玉米 + 披薩 + 蛋糕 + 愛心 + 笑臉 + 機器人 + 帽子 + 眼鏡 + 扳手 + 聖誕老人 + + 雨傘 + 沙漏 + 時鐘 + 禮物 + 燈泡 + + 鉛筆 + 迴紋針 + 剪刀 + 鎖頭 + 鑰匙 + 鎚子 + 電話 + 旗幟 + 火車 + 腳踏車 + 飛機 + 火箭 + 獎盃 + 足球 + 吉他 + 喇叭 + 鈴鐺 + 船錨 + 耳機 + 資料夾 + 圖釘 + diff --git a/features/verifysession/impl/src/main/res/values/localazy.xml b/features/verifysession/impl/src/main/res/values/localazy.xml index 24b3a7cf26..9b785044d8 100644 --- a/features/verifysession/impl/src/main/res/values/localazy.xml +++ b/features/verifysession/impl/src/main/res/values/localazy.xml @@ -3,6 +3,8 @@ "Something doesn’t seem right. Either the request timed out or the request was denied." "Confirm that the emojis below match those shown on your other session." "Compare emojis" + "Confirm that the numbers below match those shown on your other session." + "Compare numbers" "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted." "Prove it’s you in order to access your encrypted message history." "Open an existing session" diff --git a/features/verifysession/impl/src/main/res/values/strings_sas.xml b/features/verifysession/impl/src/main/res/values/strings_sas.xml new file mode 100644 index 0000000000..db6b545428 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values/strings_sas.xml @@ -0,0 +1,68 @@ + + + + Dog + Cat + Lion + Horse + Unicorn + Pig + Elephant + Rabbit + Panda + Rooster + Penguin + Turtle + Fish + Octopus + Butterfly + Flower + Tree + Cactus + Mushroom + Globe + Moon + Cloud + Fire + Banana + Apple + Strawberry + Corn + Pizza + Cake + Heart + Smiley + Robot + Hat + Glasses + Spanner + Santa + Thumbs Up + Umbrella + Hourglass + Clock + Gift + Light Bulb + Book + Pencil + Paperclip + Scissors + Lock + Key + Hammer + Telephone + Flag + Train + Bicycle + Aeroplane + Rocket + Trophy + Ball + Guitar + Trumpet + Bell + Anchor + Headphones + Folder + Pin + diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt index 403324816f..c96866cb5a 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt @@ -23,6 +23,7 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.verifysession.impl.VerifySelfSessionState.VerificationStep import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.api.verification.SessionVerificationData import io.element.android.libraries.matrix.api.verification.VerificationEmoji import io.element.android.libraries.matrix.api.verification.VerificationFlowState import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService @@ -73,7 +74,7 @@ class VerifySelfSessionPresenterTests { // Await for other device response: assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.AwaitingOtherDeviceResponse) // ChallengeReceived: - service.triggerReceiveVerificationData() + service.triggerReceiveVerificationData(SessionVerificationData.Emojis(emptyList())) val verifyingState = awaitItem() assertThat(verifyingState.verificationFlowStep).isInstanceOf(VerificationStep.Verifying::class.java) } @@ -132,7 +133,7 @@ class VerifySelfSessionPresenterTests { presenter.present() }.test { requestVerificationAndAwaitVerifyingState(service) - service.givenVerificationFlowState(VerificationFlowState.ReceivedVerificationData(emptyList())) + service.givenVerificationFlowState(VerificationFlowState.ReceivedVerificationData(SessionVerificationData.Emojis(emptyList()))) ensureAllEventsConsumed() } } @@ -156,19 +157,25 @@ class VerifySelfSessionPresenterTests { @Test fun `present - When verification is approved, the flow completes if there is no error`() = runTest { - val emojis = listOf( - VerificationEmoji("😄", "Smile") + val emojis = listOf( + VerificationEmoji(number = 30, emoji = "😀", description = "Smiley") ) - val service = FakeSessionVerificationService().apply { - givenEmojiList(emojis) - } + val service = FakeSessionVerificationService() val presenter = createVerifySelfSessionPresenter(service) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val state = requestVerificationAndAwaitVerifyingState(service) + val state = requestVerificationAndAwaitVerifyingState( + service, + SessionVerificationData.Emojis(emojis) + ) state.eventSink(VerifySelfSessionViewEvents.ConfirmVerification) - assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Verifying(emojis, Async.Loading())) + assertThat(awaitItem().verificationFlowStep).isEqualTo( + VerificationStep.Verifying( + SessionVerificationData.Emojis(emojis), + Async.Loading(), + ) + ) assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Completed) } } @@ -182,13 +189,19 @@ class VerifySelfSessionPresenterTests { }.test { val state = requestVerificationAndAwaitVerifyingState(service) state.eventSink(VerifySelfSessionViewEvents.DeclineVerification) - assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Verifying(emptyList(), Async.Loading())) + assertThat(awaitItem().verificationFlowStep).isEqualTo( + VerificationStep.Verifying( + SessionVerificationData.Emojis(emptyList()), + Async.Loading(), + ) + ) assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Canceled) } } private suspend fun ReceiveTurbine.requestVerificationAndAwaitVerifyingState( - fakeService: FakeSessionVerificationService + fakeService: FakeSessionVerificationService, + sessionVerificationData: SessionVerificationData = SessionVerificationData.Emojis(emptyList()), ): VerifySelfSessionState { var state = awaitItem() assertThat(state.verificationFlowStep).isEqualTo(VerificationStep.Initial) @@ -203,7 +216,7 @@ class VerifySelfSessionPresenterTests { // Await for other device response (again): state = awaitItem() assertThat(state.verificationFlowStep).isEqualTo(VerificationStep.AwaitingOtherDeviceResponse) - fakeService.triggerReceiveVerificationData() + fakeService.triggerReceiveVerificationData(sessionVerificationData) // Finally, ChallengeReceived: state = awaitItem() assertThat(state.verificationFlowStep).isInstanceOf(VerificationStep.Verifying::class.java) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7f13c696e8..1650196db7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,6 +3,8 @@ [versions] # Project +# Warning: version 8.2.0 is leading https://github.com/element-hq/element-x-android/issues/1971 +# which will only occurs in release mode, and so will not be detected by Maestro android_gradle_plugin = "8.1.4" kotlin = "1.9.20" ksp = "1.9.20-1.0.14" @@ -13,8 +15,8 @@ core = "1.12.0" datastore = "1.0.0" constraintlayout = "2.1.4" constraintlayout_compose = "1.0.1" -lifecycle = "2.7.0-rc01" -activity = "1.8.1" +lifecycle = "2.7.0-rc02" +activity = "1.8.2" media3 = "1.2.0" # Compose @@ -36,11 +38,11 @@ datetime = "0.4.1" serialization_json = "1.6.1" showkase = "1.0.2" appyx = "1.4.0" -sqldelight = "2.0.0" -wysiwyg = "2.18.0" +sqldelight = "2.0.1" +wysiwyg = "2.22.0" # DI -dagger = "2.48.1" +dagger = "2.49" anvil = "2.4.8-1-8" # Auto service @@ -59,16 +61,16 @@ android_desugar = "com.android.tools:desugar_jdk_libs:2.0.4" kotlin_gradle_plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } gms_google_services = "com.google.gms:google-services:4.4.0" # https://firebase.google.com/docs/android/setup#available-libraries -google_firebase_bom = "com.google.firebase:firebase-bom:32.6.0" +google_firebase_bom = "com.google.firebase:firebase-bom:32.7.0" firebase_appdistribution_gradle = { module = "com.google.firebase:firebase-appdistribution-gradle", version.ref = "firebaseAppDistribution" } # AndroidX androidx_core = { module = "androidx.core:core", version.ref = "core" } androidx_corektx = { module = "androidx.core:core-ktx", version.ref = "core" } -androidx_annotationjvm = "androidx.annotation:annotation-jvm:1.7.0" +androidx_annotationjvm = "androidx.annotation:annotation-jvm:1.7.1" androidx_datastore_preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore" } androidx_datastore_datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" } -androidx_exifinterface = "androidx.exifinterface:exifinterface:1.3.6" +androidx_exifinterface = "androidx.exifinterface:exifinterface:1.3.7" androidx_constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" } androidx_constraintlayout_compose = { module = "androidx.constraintlayout:constraintlayout-compose", version.ref = "constraintlayout_compose" } @@ -86,14 +88,15 @@ androidx_activity_activity = { module = "androidx.activity:activity", version.re androidx_activity_compose = { module = "androidx.activity:activity-compose", version.ref = "activity" } androidx_startup = "androidx.startup:startup-runtime:1.1.1" androidx_preference = "androidx.preference:preference:1.2.1" -androidx_webkit = "androidx.webkit:webkit:1.8.0" +androidx_webkit = "androidx.webkit:webkit:1.9.0" androidx_compose_bom = { module = "androidx.compose:compose-bom", version.ref = "compose_bom" } -androidx_compose_material3 = "androidx.compose.material3:material3:1.2.0-alpha11" +androidx_compose_material3 = "androidx.compose.material3:material3:1.2.0-beta01" androidx_compose_ui = { module = "androidx.compose.ui:ui" } androidx_compose_ui_tooling = { module = "androidx.compose.ui:ui-tooling" } androidx_compose_ui_tooling_preview = { module = "androidx.compose.ui:ui-tooling-preview" } androidx_compose_ui_test_manifest = { module = "androidx.compose.ui:ui-test-manifest" } +androidx_compose_ui_test_junit = { module = "androidx.compose.ui:ui-test-junit4-android" } androidx_compose_material = { module = "androidx.compose.material:material" } androidx_compose_material_icons = { module = "androidx.compose.material:material-icons-extended" } @@ -113,6 +116,7 @@ network_okhttp_bom = "com.squareup.okhttp3:okhttp-bom:4.12.0" network_okhttp_logging = { module = "com.squareup.okhttp3:logging-interceptor" } network_okhttp_okhttp = { module = "com.squareup.okhttp3:okhttp" } network_okhttp = { module = "com.squareup.okhttp3:okhttp" } +network_mockwebserver = { module = "com.squareup.okhttp3:mockwebserver" } network_retrofit = "com.squareup.retrofit2:retrofit:2.9.0" network_retrofit_converter_serialization = "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0" @@ -126,7 +130,7 @@ test_junitext = "androidx.test.ext:junit:1.1.5" test_mockk = "io.mockk:mockk:1.13.8" test_konsist = "com.lemonappdev:konsist:0.13.0" test_turbine = "app.cash.turbine:turbine:1.0.0" -test_truth = "com.google.truth:truth:1.1.5" +test_truth = "com.google.truth:truth:1.2.0" test_parameter_injector = "com.google.testparameterinjector:test-parameter-injector:1.14" test_robolectric = "org.robolectric:robolectric:4.11.1" test_appyx_junit = { module = "com.bumble.appyx:testing-junit4", version.ref = "appyx" } @@ -135,16 +139,18 @@ test_appyx_junit = { module = "com.bumble.appyx:testing-junit4", version.ref = " coil = { module = "io.coil-kt:coil", version.ref = "coil" } coil_compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" } coil_gif = { module = "io.coil-kt:coil-gif", version.ref = "coil" } +coil_test = { module = "io.coil-kt:coil-test", version.ref = "coil" } +compound = { module = "io.element.android:compound-android", version = "0.0.2" } datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" } serialization_json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization_json" } kotlinx_collections_immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.6" showkase = { module = "com.airbnb.android:showkase", version.ref = "showkase" } showkase_processor = { module = "com.airbnb.android:showkase-processor", version.ref = "showkase" } -jsoup = "org.jsoup:jsoup:1.16.2" +jsoup = "org.jsoup:jsoup:1.17.1" appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } -molecule-runtime = "app.cash.molecule:molecule-runtime:1.3.0" +molecule-runtime = "app.cash.molecule:molecule-runtime:1.3.1" timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.68" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.82" matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" } matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" } sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } @@ -154,18 +160,18 @@ sqlcipher = "net.zetetic:android-database-sqlcipher:4.5.4" sqlite = "androidx.sqlite:sqlite-ktx:2.4.0" unifiedpush = "com.github.UnifiedPush:android-connector:2.1.1" otaliastudios_transcoder = "com.otaliastudios:transcoder:0.10.5" -vanniktech_blurhash = "com.vanniktech:blurhash:0.1.0" +vanniktech_blurhash = "com.vanniktech:blurhash:0.2.0" telephoto_zoomableimage = "me.saket.telephoto:zoomable-image-coil:0.7.1" statemachine = "com.freeletics.flowredux:compose:1.2.0" maplibre = "org.maplibre.gl:android-sdk:10.2.0" maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:2.0.2" maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:2.0.2" opusencoder = "io.element.android:opusencoder:1.1.0" -kotlinpoet = "com.squareup:kotlinpoet:1.15.1" +kotlinpoet = "com.squareup:kotlinpoet:1.15.3" # Analytics -posthog = "com.posthog.android:posthog:2.0.3" -sentry = "io.sentry:sentry-android:6.34.0" +posthog = "com.posthog:posthog-android:3.0.0" +sentry = "io.sentry:sentry-android:7.1.0" matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:aa14cbcdf81af2746d20a71779ec751f971e1d7f" # Emojibase @@ -203,11 +209,11 @@ kotlin_serialization = { id = "org.jetbrains.kotlin.plugin.serialization", versi kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } anvil = { id = "com.squareup.anvil", version.ref = "anvil" } -detekt = "io.gitlab.arturbosch.detekt:1.23.3" -ktlint = "org.jlleitschuh.gradle.ktlint:11.6.1" +detekt = "io.gitlab.arturbosch.detekt:1.23.4" +ktlint = "org.jlleitschuh.gradle.ktlint:12.0.3" dependencygraph = "com.savvasdalkitsis.module-dependency-graph:0.12" -dependencycheck = "org.owasp.dependencycheck:8.4.3" -dependencyanalysis = "com.autonomousapps.dependency-analysis:1.26.0" +dependencycheck = "org.owasp.dependencycheck:9.0.7" +dependencyanalysis = "com.autonomousapps.dependency-analysis:1.27.0" paparazzi = "app.cash.paparazzi:1.3.1" kover = "org.jetbrains.kotlinx.kover:0.6.1" sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 01f330a93e..a7a990ab2a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=f2b9ed0faf8472cbe469255ae6c86eddb77076c75191741b4a462f33128dd419 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip +distributionSha256Sum=c16d517b50dd28b3f5838f0e844b7520b8f1eb610f2f29de7e4e04a1b7c9c79b +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/libraries/androidutils/build.gradle.kts b/libraries/androidutils/build.gradle.kts index 87382a2c3e..94e6d92160 100644 --- a/libraries/androidutils/build.gradle.kts +++ b/libraries/androidutils/build.gradle.kts @@ -1,4 +1,3 @@ - /* * Copyright (c) 2023 New Vector Ltd * @@ -33,6 +32,7 @@ dependencies { implementation(projects.libraries.di) implementation(projects.libraries.core) + implementation(projects.services.toolbox.api) implementation(libs.dagger) implementation(libs.timber) implementation(libs.androidx.corektx) @@ -41,4 +41,12 @@ dependencies { implementation(libs.androidx.exifinterface) implementation(libs.androidx.security.crypto) api(libs.androidx.browser) + + testImplementation(projects.tests.testutils) + testImplementation(libs.test.junit) + testImplementation(libs.test.truth) + testImplementation(libs.test.robolectric) + testImplementation(libs.coroutines.core) + testImplementation(libs.coroutines.test) + testImplementation(projects.services.toolbox.test) } diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/clipboard/ClipboardHelper.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/clipboard/ClipboardHelper.kt index 39cb719d48..3ebfeb811a 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/clipboard/ClipboardHelper.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/clipboard/ClipboardHelper.kt @@ -21,5 +21,4 @@ package io.element.android.libraries.androidutils.clipboard */ interface ClipboardHelper { fun copyPlainText(text: String) - } diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/compat/Compat.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/compat/Compat.kt index 69761ccbc2..ca00f72fb7 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/compat/Compat.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/compat/Compat.kt @@ -17,26 +17,15 @@ package io.element.android.libraries.androidutils.compat import android.content.pm.ApplicationInfo -import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.os.Build fun PackageManager.getApplicationInfoCompat(packageName: String, flags: Int): ApplicationInfo { return when { Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getApplicationInfo( - packageName, - PackageManager.ApplicationInfoFlags.of(flags.toLong()) + packageName, + PackageManager.ApplicationInfoFlags.of(flags.toLong()) ) else -> @Suppress("DEPRECATION") getApplicationInfo(packageName, flags) } } - -fun PackageManager.getPackageInfoCompat(packageName: String, flags: Int): PackageInfo { - return when { - Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getPackageInfo( - packageName, - PackageManager.PackageInfoFlags.of(flags.toLong()) - ) - else -> @Suppress("DEPRECATION") getPackageInfo(packageName, flags) - } -} diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/file/File.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/file/File.kt index d7628202c0..f8d4053feb 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/file/File.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/file/File.kt @@ -21,7 +21,6 @@ import androidx.annotation.WorkerThread import io.element.android.libraries.core.data.tryOrNull import timber.log.Timber import java.io.File -import java.util.Locale import java.util.UUID fun File.safeDelete() { @@ -56,88 +55,6 @@ fun Context.createTmpFile(baseDir: File = cacheDir, extension: String? = null): return File.createTempFile(UUID.randomUUID().toString(), suffix, baseDir).apply { mkdirs() } } -// Implementation should return true in case of success -typealias ActionOnFile = (file: File) -> Boolean - -/* ========================================================================================== - * Log - * ========================================================================================== */ - -fun lsFiles(context: Context) { - Timber.v("Content of cache dir:") - recursiveActionOnFile(context.cacheDir, ::logAction) - - Timber.v("Content of files dir:") - recursiveActionOnFile(context.filesDir, ::logAction) -} - -private fun logAction(file: File): Boolean { - if (file.isDirectory) { - Timber.v(file.toString()) - } else { - Timber.v("$file ${file.length()} bytes") - } - return true -} - -/* ========================================================================================== - * Private - * ========================================================================================== */ - -/** - * Return true in case of success. - */ -private fun recursiveActionOnFile(file: File, action: ActionOnFile): Boolean { - if (file.isDirectory) { - file.list()?.forEach { - val result = recursiveActionOnFile(File(file, it), action) - - if (!result) { - // Break the loop - return false - } - } - } - - return action.invoke(file) -} - -/** - * Get the file extension of a fileUri or a filename. - * - * @param fileUri the fileUri (can be a simple filename) - * @return the file extension, in lower case, or null is extension is not available or empty - */ -fun getFileExtension(fileUri: String): String? { - var reducedStr = fileUri - - if (reducedStr.isNotEmpty()) { - // Remove fragment - reducedStr = reducedStr.substringBeforeLast('#') - - // Remove query - reducedStr = reducedStr.substringBeforeLast('?') - - // Remove path - val filename = reducedStr.substringAfterLast('/') - - // Contrary to method MimeTypeMap.getFileExtensionFromUrl, we do not check the pattern - // See https://stackoverflow.com/questions/14320527/android-should-i-use-mimetypemap-getfileextensionfromurl-bugs - if (filename.isNotEmpty()) { - val dotPos = filename.lastIndexOf('.') - if (0 <= dotPos) { - val ext = filename.substring(dotPos + 1) - - if (ext.isNotBlank()) { - return ext.lowercase(Locale.ROOT) - } - } - } - } - - return null -} - /* ========================================================================================== * Size * ========================================================================================== */ diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/filesize/AndroidFileSizeFormatter.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/filesize/AndroidFileSizeFormatter.kt index 9cd70febcc..abf55b581c 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/filesize/AndroidFileSizeFormatter.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/filesize/AndroidFileSizeFormatter.kt @@ -22,16 +22,18 @@ import android.text.format.Formatter import com.squareup.anvil.annotations.ContributesBinding import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.ApplicationContext +import io.element.android.services.toolbox.api.sdk.BuildVersionSdkIntProvider import javax.inject.Inject @ContributesBinding(AppScope::class) class AndroidFileSizeFormatter @Inject constructor( @ApplicationContext private val context: Context, - ) : FileSizeFormatter { + private val sdkIntProvider: BuildVersionSdkIntProvider, +) : FileSizeFormatter { override fun format(fileSize: Long, useShortFormat: Boolean): String { // Since Android O, the system considers that 1kB = 1000 bytes instead of 1024 bytes. // We want to avoid that. - val normalizedSize = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) { + val normalizedSize = if (sdkIntProvider.get() <= Build.VERSION_CODES.N) { fileSize } else { // First convert the size diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/network/WifiDetector.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/network/WifiDetector.kt deleted file mode 100644 index 85b17c6ff8..0000000000 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/network/WifiDetector.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 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 io.element.android.libraries.androidutils.network - -import android.content.Context -import android.net.ConnectivityManager -import android.net.NetworkCapabilities -import androidx.core.content.getSystemService -import io.element.android.libraries.core.bool.orFalse -import timber.log.Timber - -class WifiDetector( - context: Context -) { - private val connectivityManager = context.getSystemService()!! - - fun isConnectedToWifi(): Boolean { - return connectivityManager.activeNetwork - ?.let { connectivityManager.getNetworkCapabilities(it) } - ?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) - .orFalse() - .also { Timber.d("isConnected to WiFi: $it") } - } -} diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt index 1aee986d32..422307ffd9 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt @@ -16,47 +16,20 @@ package io.element.android.libraries.androidutils.system -import android.annotation.SuppressLint -import android.annotation.TargetApi -import android.app.Activity -import android.app.NotificationManager import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.net.Uri import android.os.Build -import android.os.PowerManager import android.provider.Settings import android.widget.Toast import androidx.activity.result.ActivityResultLauncher import androidx.annotation.ChecksSdkIntAtLeast import androidx.annotation.RequiresApi -import androidx.core.content.getSystemService import io.element.android.libraries.androidutils.R import io.element.android.libraries.androidutils.compat.getApplicationInfoCompat - -/** - * Tells if the application ignores battery optimizations. - * - * Ignoring them allows the app to run in background to make background sync with the homeserver. - * This user option appears on Android M but Android O enforces its usage and kills apps not - * authorised by the user to run in background. - * - * @return true if battery optimisations are ignored - */ -fun Context.isIgnoringBatteryOptimizations(): Boolean { - // no issue before Android M, battery optimisations did not exist - return getSystemService()?.isIgnoringBatteryOptimizations(packageName) == true -} - -fun Context.isAirplaneModeOn(): Boolean { - return Settings.Global.getInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0) != 0 -} - -fun Context.isAnimationEnabled(): Boolean { - return Settings.Global.getFloat(contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f) != 0f -} +import io.element.android.libraries.core.mimetype.MimeTypes @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O) fun supportNotificationChannels() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O @@ -73,32 +46,6 @@ fun Context.getApplicationLabel(packageName: String): String { } } -/** - * Return true it the user has enabled the do not disturb mode. - */ -fun Context.isDoNotDisturbModeOn(): Boolean { - // We cannot use NotificationManagerCompat here. - val setting = getSystemService()!!.currentInterruptionFilter - - return setting == NotificationManager.INTERRUPTION_FILTER_NONE || - setting == NotificationManager.INTERRUPTION_FILTER_ALARMS -} - -/** - * display the system dialog for granting this permission. If previously granted, the - * system will not show it (so you should call this method). - * - * Note: If the user finally does not grant the permission, PushManager.isBackgroundSyncAllowed() - * will return false and the notification privacy will fallback to "LOW_DETAIL". - */ -@SuppressLint("BatteryLife") -fun Context.requestDisablingBatteryOptimization(activityResultLauncher: ActivityResultLauncher) { - val intent = Intent() - intent.action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS - intent.data = Uri.parse("package:$packageName") - activityResultLauncher.launch(intent) -} - // ============================================================================================================== // Clipboard helper // ============================================================================================================== @@ -156,32 +103,6 @@ fun Context.openAppSettingsPage( } } -/** - * Shows notification system settings for the given channel id. - */ -@TargetApi(Build.VERSION_CODES.O) -fun Activity.startNotificationChannelSettingsIntent(channelID: String) { - if (!supportNotificationChannels()) return - val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS).apply { - putExtra(Settings.EXTRA_APP_PACKAGE, packageName) - putExtra(Settings.EXTRA_CHANNEL_ID, channelID) - } - startActivity(intent) -} - -fun Context.startAddGoogleAccountIntent( - activityResultLauncher: ActivityResultLauncher, - noActivityFoundMessage: String = getString(R.string.error_no_compatible_app_found), -) { - val intent = Intent(Settings.ACTION_ADD_ACCOUNT) - intent.putExtra(Settings.EXTRA_ACCOUNT_TYPES, arrayOf("com.google")) - try { - activityResultLauncher.launch(intent) - } catch (activityNotFoundException: ActivityNotFoundException) { - toast(noActivityFoundMessage) - } -} - @RequiresApi(Build.VERSION_CODES.O) fun Context.startInstallFromSourceIntent( activityResultLauncher: ActivityResultLauncher, @@ -205,7 +126,7 @@ fun Context.startSharePlainTextIntent( noActivityFoundMessage: String = getString(R.string.error_no_compatible_app_found), ) { val share = Intent(Intent.ACTION_SEND) - share.type = "text/plain" + share.type = MimeTypes.PlainText share.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT) // Add data to the intent, the receiving app will decide what to do with it. share.putExtra(Intent.EXTRA_SUBJECT, subject) @@ -227,20 +148,6 @@ fun Context.startSharePlainTextIntent( } } -fun Context.startImportTextFromFileIntent( - activityResultLauncher: ActivityResultLauncher, - noActivityFoundMessage: String = getString(R.string.error_no_compatible_app_found), -) { - val intent = Intent(Intent.ACTION_GET_CONTENT).apply { - type = "text/plain" - } - try { - activityResultLauncher.launch(intent) - } catch (activityNotFoundException: ActivityNotFoundException) { - toast(noActivityFoundMessage) - } -} - @Suppress("SwallowedException") fun Context.openUrlInExternalApp( url: String, diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/ui/DimensionConverter.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/ui/DimensionConverter.kt deleted file mode 100644 index beead850b3..0000000000 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/ui/DimensionConverter.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 io.element.android.libraries.androidutils.ui - -import android.content.res.Resources -import android.util.TypedValue -import androidx.annotation.Px - -class DimensionConverter(private val resources: Resources) { - - @Px - fun dpToPx(dp: Int): Int { - return TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, - dp.toFloat(), - resources.displayMetrics - ).toInt() - } - - @Px - fun spToPx(sp: Int): Int { - return TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_SP, - sp.toFloat(), - resources.displayMetrics - ).toInt() - } - - fun pxToDp(@Px px: Int): Int { - return (px.toFloat() / resources.displayMetrics.density).toInt() - } -} diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/ui/View.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/ui/View.kt index 8bbaf1e5c3..8546bfa66c 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/ui/View.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/ui/View.kt @@ -36,15 +36,6 @@ fun View.showKeyboard(andRequestFocus: Boolean = false) { imm?.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT) } -fun View.setHorizontalPadding(padding: Int) { - setPadding( - padding, - paddingTop, - padding, - paddingBottom - ) -} - suspend fun View.awaitWindowFocus() = suspendCancellableCoroutine { continuation -> if (hasWindowFocus()) { continuation.resume(Unit) diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/uri/UriExtensions.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/uri/UriExtensions.kt index 1375104b79..4a2f48457e 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/uri/UriExtensions.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/uri/UriExtensions.kt @@ -18,12 +18,6 @@ package io.element.android.libraries.androidutils.uri import android.net.Uri -const val ASSET_FILE_PATH_ROOT = "android_asset" const val IGNORED_SCHEMA = "ignored" -fun Uri.isIgnored() = scheme == IGNORED_SCHEMA - fun createIgnoredUri(path: String): Uri = Uri.parse("$IGNORED_SCHEMA://$path") - -val Uri.firstPathSegment: String? - get() = pathSegments.firstOrNull() diff --git a/libraries/androidutils/src/main/res/values-hu/translations.xml b/libraries/androidutils/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..dbbc088ed4 --- /dev/null +++ b/libraries/androidutils/src/main/res/values-hu/translations.xml @@ -0,0 +1,4 @@ + + + "Nem található kompatibilis alkalmazás a művelet kezeléséhez." + diff --git a/libraries/androidutils/src/main/res/values-in/translations.xml b/libraries/androidutils/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..b593b49812 --- /dev/null +++ b/libraries/androidutils/src/main/res/values-in/translations.xml @@ -0,0 +1,4 @@ + + + "Tidak ada aplikasi yang kompatibel yang ditemukan untuk menangani tindakan ini." + diff --git a/libraries/androidutils/src/test/kotlin/io/element/android/libraries/androidutils/filesize/AndroidFileSizeFormatterTest.kt b/libraries/androidutils/src/test/kotlin/io/element/android/libraries/androidutils/filesize/AndroidFileSizeFormatterTest.kt new file mode 100644 index 0000000000..4d83a2367a --- /dev/null +++ b/libraries/androidutils/src/test/kotlin/io/element/android/libraries/androidutils/filesize/AndroidFileSizeFormatterTest.kt @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.androidutils.filesize + +import android.os.Build +import com.google.common.truth.Truth.assertThat +import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvider +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +@RunWith(RobolectricTestRunner::class) +class AndroidFileSizeFormatterTest { + @Test + fun `test api 24 long format`() { + val sut = createAndroidFileSizeFormatter(sdkLevel = Build.VERSION_CODES.N) + assertThat(sut.format(1, useShortFormat = false)).isEqualTo("1.00B") + assertThat(sut.format(1000, useShortFormat = false)).isEqualTo("0.98KB") + assertThat(sut.format(1024, useShortFormat = false)).isEqualTo("1.00KB") + assertThat(sut.format(1024 * 1024, useShortFormat = false)).isEqualTo("1.00MB") + assertThat(sut.format(1024 * 1024 * 1024, useShortFormat = false)).isEqualTo("1.00GB") + } + + @Test + fun `test api 26 long format`() { + val sut = createAndroidFileSizeFormatter(sdkLevel = Build.VERSION_CODES.O) + assertThat(sut.format(1, useShortFormat = false)).isEqualTo("1.00B") + assertThat(sut.format(1000, useShortFormat = false)).isEqualTo("0.98KB") + assertThat(sut.format(1024 * 1024, useShortFormat = false)).isEqualTo("0.95MB") + assertThat(sut.format(1024 * 1024 * 1024, useShortFormat = false)).isEqualTo("0.93GB") + } + + @Test + fun `test api 24 short format`() { + val sut = createAndroidFileSizeFormatter(sdkLevel = Build.VERSION_CODES.N) + assertThat(sut.format(1, useShortFormat = true)).isEqualTo("1.0B") + assertThat(sut.format(1000, useShortFormat = true)).isEqualTo("0.98KB") + assertThat(sut.format(1024, useShortFormat = true)).isEqualTo("1.0KB") + assertThat(sut.format(1024 * 1024, useShortFormat = true)).isEqualTo("1.0MB") + assertThat(sut.format(1024 * 1024 * 1024, useShortFormat = true)).isEqualTo("1.0GB") + } + + @Test + fun `test api 26 short format`() { + val sut = createAndroidFileSizeFormatter(sdkLevel = Build.VERSION_CODES.O) + assertThat(sut.format(1, useShortFormat = true)).isEqualTo("1.0B") + assertThat(sut.format(1000, useShortFormat = true)).isEqualTo("0.98KB") + assertThat(sut.format(1024 * 1024, useShortFormat = true)).isEqualTo("0.95MB") + assertThat(sut.format(1024 * 1024 * 1024, useShortFormat = true)).isEqualTo("0.93GB") + } + + private fun createAndroidFileSizeFormatter(sdkLevel: Int) = AndroidFileSizeFormatter( + context = RuntimeEnvironment.getApplication(), + sdkIntProvider = FakeBuildVersionSdkIntProvider(sdkInt = sdkLevel) + ) +} diff --git a/libraries/architecture/build.gradle.kts b/libraries/architecture/build.gradle.kts index 68a25ead04..aff0e48c69 100644 --- a/libraries/architecture/build.gradle.kts +++ b/libraries/architecture/build.gradle.kts @@ -15,6 +15,7 @@ */ plugins { id("io.element.android-compose-library") + id("kotlin-parcelize") } android { diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/BackstackNode.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/BackstackNode.kt deleted file mode 100644 index deb6d0e63b..0000000000 --- a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/BackstackNode.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.libraries.architecture - -import androidx.compose.runtime.Stable -import com.bumble.appyx.core.children.ChildEntry -import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.navigation.model.combined.plus -import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel -import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.node.ParentNode -import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.navmodel.backstack.BackStack - -/** - * This class is just an helper for configuring a backstack directly in the constructor. - * With this we can more easily use constructor injection without having a secondary constructor to create the [BackStack] instance. - * Can be used instead of [ParentNode] in flow nodes. - */ -@Stable -abstract class BackstackNode( - val backstack: BackStack, - buildContext: BuildContext, - plugins: List, - val permanentNavModel: PermanentNavModel = PermanentNavModel(emptySet(), null), - childKeepMode: ChildEntry.KeepMode = ChildEntry.KeepMode.KEEP, -) : ParentNode( - navModel = backstack + permanentNavModel, - buildContext = buildContext, - plugins = plugins, - childKeepMode = childKeepMode, -) { - override fun onBuilt() { - super.onBuilt() - lifecycle.logLifecycle(this::class.java.simpleName) - whenChildAttached { _, child -> - // BackstackNode will be logged by their parent. - if (child !is BackstackNode<*>) { - child.lifecycle.logLifecycle(child::class.java.simpleName) - } - } - } -} diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/BaseFlowNode.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/BaseFlowNode.kt new file mode 100644 index 0000000000..f55706593e --- /dev/null +++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/BaseFlowNode.kt @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.architecture + +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.ui.Modifier +import com.bumble.appyx.core.children.ChildEntry +import com.bumble.appyx.core.composable.Children +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.navigation.model.combined.plus +import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel +import com.bumble.appyx.core.navigation.transition.TransitionHandler +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.node.ParentNode +import com.bumble.appyx.core.plugin.Plugin +import com.bumble.appyx.navmodel.backstack.BackStack +import com.bumble.appyx.navmodel.backstack.transitionhandler.rememberBackstackFader +import com.bumble.appyx.navmodel.backstack.transitionhandler.rememberBackstackSlider +import io.element.android.libraries.architecture.overlay.Overlay + +/** + * This class is a [ParentNode] that contains a [BackStack] and an [Overlay]. It is used to represent a flow in the app. + * Should be used instead of [ParentNode] in flow nodes. + */ +@Stable +abstract class BaseFlowNode( + val backstack: BackStack, + buildContext: BuildContext, + plugins: List, + val overlay: Overlay = Overlay(null), + val permanentNavModel: PermanentNavModel = PermanentNavModel(emptySet(), null), + childKeepMode: ChildEntry.KeepMode = ChildEntry.KeepMode.KEEP, +) : ParentNode( + navModel = overlay + backstack + permanentNavModel, + buildContext = buildContext, + plugins = plugins, + childKeepMode = childKeepMode, +) { + override fun onBuilt() { + super.onBuilt() + lifecycle.logLifecycle(this::class.java.simpleName) + whenChildAttached { _, child -> + // BackstackNode will be logged by their parent. + if (child !is BaseFlowNode<*>) { + child.lifecycle.logLifecycle(child::class.java.simpleName) + } + } + } +} + +@Composable +inline fun BaseFlowNode.BackstackView( + modifier: Modifier = Modifier, + transitionHandler: TransitionHandler = rememberBackstackSlider( + transitionSpec = { spring(stiffness = Spring.StiffnessMediumLow) }, + ), +) { + Children( + modifier = modifier, + navModel = backstack, + transitionHandler = transitionHandler, + ) +} + +@Composable +inline fun BaseFlowNode.OverlayView( + modifier: Modifier = Modifier, + transitionHandler: TransitionHandler = rememberBackstackFader(), +) { + Children( + modifier = modifier, + navModel = overlay, + transitionHandler = transitionHandler, + ) +} + +@Composable +inline fun BaseFlowNode.BackstackWithOverlayBox( + modifier: Modifier = Modifier, + content: @Composable BoxScope.() -> Unit = {}, +) { + Box(modifier = modifier) { + BackstackView() + OverlayView() + content() + } +} diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeFactories.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeFactories.kt index 6073b45351..0e6bf1505d 100644 --- a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeFactories.kt +++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeFactories.kt @@ -21,27 +21,36 @@ import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -inline fun Node.createNode(context: BuildContext, plugins: List = emptyList()): NODE { +inline fun Node.createNode( + buildContext: BuildContext, + plugins: List = emptyList() +): N { val bindings: NodeFactoriesBindings = bindings() - return bindings.createNode(context, plugins) + return bindings.createNode(buildContext, plugins) } -inline fun Context.createNode(context: BuildContext, plugins: List = emptyList()): NODE { +inline fun Context.createNode( + buildContext: BuildContext, + plugins: List = emptyList() +): N { val bindings: NodeFactoriesBindings = bindings() - return bindings.createNode(context, plugins) + return bindings.createNode(buildContext, plugins) } -inline fun NodeFactoriesBindings.createNode(context: BuildContext, plugins: List = emptyList()): NODE { - val nodeClass = NODE::class.java +inline fun NodeFactoriesBindings.createNode( + buildContext: BuildContext, + plugins: List = emptyList() +): N { + val nodeClass = N::class.java val nodeFactoryMap = nodeFactories() // Note to developers: If you got the error below, make sure to build again after // clearing the cache (sometimes several times) to let Dagger generate the NodeFactory. val nodeFactory = nodeFactoryMap[nodeClass] ?: error("Cannot find NodeFactory for ${nodeClass.name}.") @Suppress("UNCHECKED_CAST") - val castedNodeFactory = nodeFactory as? AssistedNodeFactory - val node = castedNodeFactory?.create(context, plugins) - return node as NODE + val castedNodeFactory = nodeFactory as? AssistedNodeFactory + val node = castedNodeFactory?.create(buildContext, plugins) + return node as N } interface NodeFactoriesBindings { diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/overlay/HideOverlayBackPressHandler.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/overlay/HideOverlayBackPressHandler.kt new file mode 100644 index 0000000000..38149f2e92 --- /dev/null +++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/overlay/HideOverlayBackPressHandler.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.architecture.overlay + +import com.bumble.appyx.core.navigation.backpresshandlerstrategies.BaseBackPressHandlerStrategy +import com.bumble.appyx.navmodel.backstack.BackStack +import com.bumble.appyx.navmodel.backstack.BackStackElements +import io.element.android.libraries.architecture.overlay.operation.Hide +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +class HideOverlayBackPressHandler + : BaseBackPressHandlerStrategy() { + + override val canHandleBackPressFlow: Flow by lazy { + navModel.elements.map(::areThereElements) + } + + private fun areThereElements(elements: BackStackElements) = + elements.isNotEmpty() + + override fun onBackPressed() { + navModel.accept(Hide()) + } +} diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/overlay/Overlay.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/overlay/Overlay.kt new file mode 100644 index 0000000000..e8f3e3ebc5 --- /dev/null +++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/overlay/Overlay.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.architecture.overlay + +import com.bumble.appyx.core.navigation.BaseNavModel +import com.bumble.appyx.core.navigation.NavElements +import com.bumble.appyx.core.navigation.backpresshandlerstrategies.BackPressHandlerStrategy +import com.bumble.appyx.core.navigation.onscreen.OnScreenStateResolver +import com.bumble.appyx.core.navigation.operationstrategies.ExecuteImmediately +import com.bumble.appyx.core.navigation.operationstrategies.OperationStrategy +import com.bumble.appyx.core.state.SavedStateMap +import com.bumble.appyx.navmodel.backstack.BackStack +import com.bumble.appyx.navmodel.backstack.BackStackOnScreenResolver + +class Overlay( + savedStateMap: SavedStateMap?, + key: String = requireNotNull(Overlay::class.qualifiedName), + backPressHandler: BackPressHandlerStrategy = HideOverlayBackPressHandler(), + operationStrategy: OperationStrategy = ExecuteImmediately(), + screenResolver: OnScreenStateResolver = BackStackOnScreenResolver, +) : BaseNavModel( + backPressHandler = backPressHandler, + screenResolver = screenResolver, + operationStrategy = operationStrategy, + finalState = BackStack.State.DESTROYED, + savedStateMap = savedStateMap, + key = key, +) { + + override val initialElements: NavElements + get() = emptyList() +} diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/overlay/operation/Hide.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/overlay/operation/Hide.kt new file mode 100644 index 0000000000..e782d4537f --- /dev/null +++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/overlay/operation/Hide.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.architecture.overlay.operation + +import com.bumble.appyx.navmodel.backstack.BackStack +import com.bumble.appyx.navmodel.backstack.BackStackElements +import com.bumble.appyx.navmodel.backstack.activeIndex +import io.element.android.libraries.architecture.overlay.Overlay +import kotlinx.parcelize.Parcelize + +@Parcelize +class Hide : OverlayOperation { + + override fun isApplicable(elements: BackStackElements): Boolean = + elements.any { it.targetState == BackStack.State.ACTIVE } + + override fun invoke( + elements: BackStackElements + ): BackStackElements { + val hideIndex = elements.activeIndex + require(hideIndex != -1) { "Nothing to hide, state=$elements" } + return elements.mapIndexed { index, element -> + when (index) { + hideIndex -> element.transitionTo( + newTargetState = BackStack.State.DESTROYED, + operation = this + ) + else -> element + } + } + } + + override fun equals(other: Any?): Boolean = this.javaClass == other?.javaClass + + override fun hashCode(): Int = this.javaClass.hashCode() +} + +fun Overlay.hide() { + accept(Hide()) +} diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/overlay/operation/OverlayOperation.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/overlay/operation/OverlayOperation.kt new file mode 100644 index 0000000000..83bb82ef4f --- /dev/null +++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/overlay/operation/OverlayOperation.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.architecture.overlay.operation + +import com.bumble.appyx.core.navigation.Operation +import com.bumble.appyx.navmodel.backstack.BackStack + +interface OverlayOperation : Operation diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/overlay/operation/Show.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/overlay/operation/Show.kt new file mode 100644 index 0000000000..90561127ef --- /dev/null +++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/overlay/operation/Show.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.architecture.overlay.operation + +import com.bumble.appyx.core.navigation.NavKey +import com.bumble.appyx.navmodel.backstack.BackStack +import com.bumble.appyx.navmodel.backstack.BackStackElement +import com.bumble.appyx.navmodel.backstack.BackStackElements +import com.bumble.appyx.navmodel.backstack.activeElement +import io.element.android.libraries.architecture.overlay.Overlay +import kotlinx.parcelize.Parcelize +import kotlinx.parcelize.RawValue + +@Parcelize +data class Show( + private val element: @RawValue T +) : OverlayOperation { + + override fun isApplicable(elements: BackStackElements): Boolean = + element != elements.activeElement + + override fun invoke(elements: BackStackElements): BackStackElements = listOf( + BackStackElement( + key = NavKey(element), + fromState = BackStack.State.CREATED, + targetState = BackStack.State.ACTIVE, + operation = this + ) + ) +} + +fun Overlay.show(element: T) { + accept(Show(element)) +} diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt index af2bca157b..6a97757b55 100644 --- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt +++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt @@ -60,4 +60,11 @@ object MimeTypes { else -> OctetStream } } + + fun hasSubtype(mimeType: String): Boolean { + val components = mimeType.split("/") + if (components.size != 2) return false + val subType = components.last() + return subType.isNotBlank() && subType != "*" + } } diff --git a/libraries/designsystem/build.gradle.kts b/libraries/designsystem/build.gradle.kts index 1a1e44366b..6cb1f4f7d2 100644 --- a/libraries/designsystem/build.gradle.kts +++ b/libraries/designsystem/build.gradle.kts @@ -35,7 +35,7 @@ android { } dependencies { - api(projects.libraries.theme) + api(libs.compound) // Should not be there, but this is a POC implementation(libs.coil.compose) implementation(libs.vanniktech.blurhash) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/ElementLogoAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/ElementLogoAtom.kt index 512f0c3ed1..e08f156340 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/ElementLogoAtom.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/ElementLogoAtom.kt @@ -33,13 +33,13 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.R import io.element.android.libraries.designsystem.modifiers.blurCompat import io.element.android.libraries.designsystem.modifiers.blurredShapeShadow import io.element.android.libraries.designsystem.modifiers.canUseBlurMaskFilter import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.theme.ElementTheme @Composable fun ElementLogoAtom( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/PlaceholderAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/PlaceholderAtom.kt index fce7378c1e..a892667379 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/PlaceholderAtom.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/PlaceholderAtom.kt @@ -29,7 +29,7 @@ import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.theme.placeholderBackground -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable fun PlaceholderAtom( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RedIndicatorAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RedIndicatorAtom.kt index c14f961a8f..772bc723d7 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RedIndicatorAtom.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RedIndicatorAtom.kt @@ -30,7 +30,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable fun RedIndicatorAtom( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoundedIconAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoundedIconAtom.kt index 8c8f009618..1bb7915fa3 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoundedIconAtom.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoundedIconAtom.kt @@ -36,7 +36,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.temporaryColorBgSpecial -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme /** * RoundedIconAtom is an atom which displays an icon inside a rounded container. @@ -70,7 +70,7 @@ fun RoundedIconAtom( tint = tint, resourceId = resourceId, imageVector = imageVector, - contentDescription = "", + contentDescription = null, ) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/UnreadIndicatorAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/UnreadIndicatorAtom.kt index 5ee00c5288..99b0c8363c 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/UnreadIndicatorAtom.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/UnreadIndicatorAtom.kt @@ -29,7 +29,7 @@ import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.theme.unreadIndicator -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable fun UnreadIndicatorAtom( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/DialogLikeBannerMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/DialogLikeBannerMolecule.kt index 1be5f12b0f..da8088bced 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/DialogLikeBannerMolecule.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/DialogLikeBannerMolecule.kt @@ -30,6 +30,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button @@ -37,8 +39,6 @@ import io.element.android.libraries.designsystem.theme.components.ButtonSize import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -71,7 +71,7 @@ fun DialogLikeBannerMolecule( if (onDismissClicked != null) { Icon( modifier = Modifier.clickable(onClick = onDismissClicked), - resourceId = CommonDrawables.ic_compound_close, + imageVector = CompoundIcons.Close, contentDescription = stringResource(CommonStrings.action_close) ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt index d4bf94a487..7bb0e22771 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt @@ -29,12 +29,12 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.placeholderBackground -import io.element.android.libraries.theme.ElementTheme @Composable fun IconTitlePlaceholdersRowMolecule( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitleSubtitleMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitleSubtitleMolecule.kt index 52d8eb5baf..e945795f4a 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitleSubtitleMolecule.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitleSubtitleMolecule.kt @@ -28,13 +28,13 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.R +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtomSize import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme /** * IconTitleSubtitleMolecule is a molecule which displays an icon, a title and a subtitle. @@ -90,7 +90,7 @@ fun IconTitleSubtitleMolecule( @Composable internal fun IconTitleSubtitleMoleculePreview() = ElementPreview { IconTitleSubtitleMolecule( - iconResourceId = R.drawable.ic_compound_chat, + iconImageVector = CompoundIcons.Chat, title = "Title", subTitle = "Subtitle", ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/InfoListItemMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/InfoListItemMolecule.kt index 23ed6a1af6..af473d8d45 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/InfoListItemMolecule.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/InfoListItemMolecule.kt @@ -29,11 +29,11 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables @Composable fun InfoListItemMolecule( @@ -78,25 +78,25 @@ internal fun InfoListItemMoleculePreview() { ) { InfoListItemMolecule( message = { Text("A single item") }, - icon = { Icon(resourceId = CommonDrawables.ic_compound_info_solid, contentDescription = null) }, + icon = { Icon(imageVector = CompoundIcons.InfoSolid, contentDescription = null) }, position = InfoListItemPosition.Single, backgroundColor = color, ) InfoListItemMolecule( message = { Text("A top item") }, - icon = { Icon(resourceId = CommonDrawables.ic_compound_info_solid, contentDescription = null) }, + icon = { Icon(imageVector = CompoundIcons.InfoSolid, contentDescription = null) }, position = InfoListItemPosition.Top, backgroundColor = color, ) InfoListItemMolecule( message = { Text("A middle item") }, - icon = { Icon(resourceId = CommonDrawables.ic_compound_info_solid, contentDescription = null) }, + icon = { Icon(imageVector = CompoundIcons.InfoSolid, contentDescription = null) }, position = InfoListItemPosition.Middle, backgroundColor = color, ) InfoListItemMolecule( message = { Text("A bottom item") }, - icon = { Icon(resourceId = CommonDrawables.ic_compound_info_solid, contentDescription = null) }, + icon = { Icon(imageVector = CompoundIcons.InfoSolid, contentDescription = null) }, position = InfoListItemPosition.Bottom, backgroundColor = color, ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/organisms/InfoListOrganism.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/organisms/InfoListOrganism.kt index ebebefbdba..47ae6d46d0 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/organisms/InfoListOrganism.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/organisms/InfoListOrganism.kt @@ -35,7 +35,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/FlowStepPage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/FlowStepPage.kt index 804e08ab2b..c2e521cc81 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/FlowStepPage.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/FlowStepPage.kt @@ -25,8 +25,10 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.R +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.components.button.BackButton @@ -36,7 +38,6 @@ import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.theme.ElementTheme /** * A Page with: @@ -48,7 +49,7 @@ import io.element.android.libraries.theme.ElementTheme @OptIn(ExperimentalMaterial3Api::class) @Composable fun FlowStepPage( - iconResourceId: Int?, + iconVector: ImageVector?, title: String, modifier: Modifier = Modifier, onBackClicked: (() -> Unit)? = null, @@ -73,7 +74,7 @@ fun FlowStepPage( }, header = { IconTitleSubtitleMolecule( - iconResourceId = iconResourceId, + iconImageVector = iconVector, title = title, subTitle = subTitle, ) @@ -96,7 +97,7 @@ internal fun FlowStepPagePreview() = ElementPreview { onBackClicked = {}, title = "Title", subTitle = "Subtitle", - iconResourceId = R.drawable.ic_compound_computer, + iconVector = CompoundIcons.Computer, content = { Box( Modifier diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/HeaderFooterPage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/HeaderFooterPage.kt index 5af89a600e..0c94df7bf2 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/HeaderFooterPage.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/HeaderFooterPage.kt @@ -30,7 +30,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme /** * @param modifier Classical modifier. diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/OnBoardingPage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/OnBoardingPage.kt index 26fceb5687..7a66ac97a5 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/OnBoardingPage.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/OnBoardingPage.kt @@ -33,7 +33,7 @@ import io.element.android.libraries.designsystem.R import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme /** * Page for onboarding screens, with content and optional footer. diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/SunsetPage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/SunsetPage.kt index 7c9df66498..476545befc 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/SunsetPage.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/SunsetPage.kt @@ -37,14 +37,14 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.theme.ForcedDarkElementTheme import io.element.android.libraries.designsystem.R -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.text.withColoredPeriod import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme -import io.element.android.libraries.theme.ForcedDarkElementTheme @Composable fun SunsetPage( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/colors/AvatarColorsProvider.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/colors/AvatarColorsProvider.kt index bf195e8160..b4bdee33fd 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/colors/AvatarColorsProvider.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/colors/AvatarColorsProvider.kt @@ -17,8 +17,8 @@ package io.element.android.libraries.designsystem.colors import androidx.collection.LruCache -import io.element.android.libraries.theme.colors.avatarColorsDark -import io.element.android.libraries.theme.colors.avatarColorsLight +import io.element.android.compound.theme.avatarColorsDark +import io.element.android.compound.theme.avatarColorsLight object AvatarColorsProvider { private val cache = LruCache(200) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt index c44a1140f0..5742438641 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt @@ -100,6 +100,8 @@ import coil.request.DefaultRequestOptions import coil.request.ImageRequest import com.airbnb.android.showkase.annotation.ShowkaseComposable import com.vanniktech.blurhash.BlurHash +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.R import io.element.android.libraries.designsystem.colors.AvatarColorsProvider import io.element.android.libraries.designsystem.components.avatar.AvatarData @@ -111,8 +113,6 @@ import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.MediumTopAppBar import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.Dispatchers @@ -509,7 +509,7 @@ internal fun BloomPreview() { actions = { IconButton(onClick = {}) { Icon( - resourceId = CommonDrawables.ic_compound_share_android, + imageVector = CompoundIcons.ShareAndroid, contentDescription = null, ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ClickableLinkText.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ClickableLinkText.kt index 17e6eec258..701d2945ba 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ClickableLinkText.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ClickableLinkText.kt @@ -40,10 +40,10 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.tooling.preview.Preview import androidx.core.text.util.LinkifyCompat +import io.element.android.compound.theme.LinkColor import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.LinkColor import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.persistentMapOf @@ -142,7 +142,7 @@ fun ClickableLinkText( fun AnnotatedString.linkify(linkStyle: SpanStyle): AnnotatedString { val original = this val spannable = SpannableString(this.text) - LinkifyCompat.addLinks(spannable, Linkify.WEB_URLS or Linkify.PHONE_NUMBERS) + LinkifyCompat.addLinks(spannable, Linkify.WEB_URLS or Linkify.PHONE_NUMBERS or Linkify.EMAIL_ADDRESSES) val spans = spannable.getSpans(0, spannable.length, URLSpan::class.java) return buildAnnotatedString { diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledCheckbox.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledCheckbox.kt index 2c9c776473..4076be8653 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledCheckbox.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledCheckbox.kt @@ -32,8 +32,8 @@ import io.element.android.libraries.designsystem.theme.components.Text fun LabelledCheckbox( checked: Boolean, text: String, + onCheckedChange: (Boolean) -> Unit, modifier: Modifier = Modifier, - onCheckedChange: (Boolean) -> Unit = {}, enabled: Boolean = true, ) { Row( @@ -60,6 +60,7 @@ internal fun LabelledCheckboxPreview() = ElementThemedPreview { ContentToPreview private fun ContentToPreview() { LabelledCheckbox( checked = true, + onCheckedChange = {}, text = "Some text", ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledOutlinedTextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledOutlinedTextField.kt index 3658db5206..9b31970da0 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledOutlinedTextField.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledOutlinedTextField.kt @@ -29,18 +29,18 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.theme.components.OutlinedTextField import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable fun LabelledOutlinedTextField( label: String, value: String, + onValueChange: (String) -> Unit, modifier: Modifier = Modifier, placeholder: String? = null, singleLine: Boolean = false, maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, keyboardOptions: KeyboardOptions = KeyboardOptions.Default, - onValueChange: (String) -> Unit = {}, ) { Column( modifier = modifier, @@ -72,11 +72,13 @@ internal fun LabelledOutlinedTextFieldPreview() = ElementPreview { LabelledOutlinedTextField( label = "Room name", value = "", + onValueChange = {}, placeholder = "e.g. Product Sprint", ) LabelledOutlinedTextField( label = "Room name", value = "a room name", + onValueChange = {}, placeholder = "e.g. Product Sprint", ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt index 64edf11c81..027aeb8f95 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt @@ -29,18 +29,18 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextField -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable fun LabelledTextField( label: String, value: String, + onValueChange: (String) -> Unit, modifier: Modifier = Modifier, placeholder: String? = null, singleLine: Boolean = false, maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, keyboardOptions: KeyboardOptions = KeyboardOptions.Default, - onValueChange: (String) -> Unit = {}, ) { Column( modifier = modifier, @@ -72,11 +72,13 @@ internal fun LabelledTextFieldPreview() = ElementPreview { LabelledTextField( label = "Room name", value = "", + onValueChange = {}, placeholder = "e.g. Product Sprint", ) LabelledTextField( label = "Room name", value = "a room name", + onValueChange = {}, placeholder = "e.g. Product Sprint", ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt index 79ff622301..11bed90654 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt @@ -28,7 +28,7 @@ import io.element.android.libraries.designsystem.R import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.theme.components.Icon -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable fun PinIcon( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt index 4ce4aa8f14..b1e7bc6be0 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt @@ -27,18 +27,19 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import coil.compose.AsyncImage +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.colors.AvatarColorsProvider import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.debugPlaceholderAvatar import io.element.android.libraries.designsystem.text.toSp import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme import timber.log.Timber @Composable @@ -96,7 +97,9 @@ private fun InitialsAvatar( val ratio = fontSize.value / originalFont.fontSize.value val lineHeight = originalFont.lineHeight * ratio Text( - modifier = Modifier.align(Alignment.Center), + modifier = Modifier + .clearAndSetSemantics {} + .align(Alignment.Center), text = avatarData.initial, style = originalFont.copy(fontSize = fontSize, lineHeight = lineHeight, letterSpacing = 0.sp), color = avatarColors.foreground, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt index 0b4264e8e6..9a5209ebfb 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt @@ -25,7 +25,7 @@ enum class AvatarSize(val dp: Dp) { RoomHeader(96.dp), RoomListItem(52.dp), - ForwardRoomListItem(36.dp), + RoomSelectRoomListItem(36.dp), UserPreference(56.dp), diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/UserAvatarPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/UserAvatarPreview.kt index f3681bbb52..0fa4ca35f8 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/UserAvatarPreview.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/UserAvatarPreview.kt @@ -24,10 +24,10 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.compound.theme.avatarColorsLight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.colors.avatarColorsLight @PreviewsDayNight @Composable diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/BackButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/BackButton.kt index 30c8b95d74..09e2390ade 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/BackButton.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/BackButton.kt @@ -16,17 +16,17 @@ package io.element.android.libraries.designsystem.components.button -import androidx.annotation.DrawableRes import androidx.compose.foundation.layout.Column import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -34,7 +34,7 @@ fun BackButton( onClick: () -> Unit, modifier: Modifier = Modifier, // TODO Handle RTL languages - @DrawableRes resourceId: Int = CommonDrawables.ic_compound_arrow_left, + imageVector: ImageVector = CompoundIcons.ArrowLeft, contentDescription: String = stringResource(CommonStrings.action_back), enabled: Boolean = true, ) { @@ -43,7 +43,7 @@ fun BackButton( onClick = onClick, enabled = enabled, ) { - Icon(resourceId = resourceId, contentDescription = contentDescription) + Icon(imageVector, contentDescription = contentDescription) } } @@ -51,7 +51,7 @@ fun BackButton( @Composable internal fun BackButtonPreview() = ElementThemedPreview { Column { - BackButton(onClick = { }, enabled = true, contentDescription = "Back") - BackButton(onClick = { }, enabled = false, contentDescription = "Back") + BackButton(onClick = { }, enabled = true) + BackButton(onClick = { }, enabled = false) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/MainActionButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/MainActionButton.kt index 2ab6b56808..c24507327d 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/MainActionButton.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/MainActionButton.kt @@ -16,7 +16,6 @@ package io.element.android.libraries.designsystem.components.button -import androidx.annotation.DrawableRes import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Column @@ -32,19 +31,20 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme @Composable fun MainActionButton( title: String, - @DrawableRes iconResourceId: Int, + imageVector: ImageVector, onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, @@ -63,7 +63,7 @@ fun MainActionButton( ) { val tintColor = if (enabled) LocalContentColor.current else MaterialTheme.colorScheme.secondary Icon( - resourceId = iconResourceId, + imageVector = imageVector, contentDescription = contentDescription, tint = tintColor, ) @@ -89,13 +89,13 @@ private fun ContentsToPreview() { Row(Modifier.padding(10.dp)) { MainActionButton( title = "Share", - iconResourceId = CommonDrawables.ic_compound_share_android, + imageVector = CompoundIcons.ShareAndroid, onClick = { }, ) Spacer(modifier = Modifier.width(20.dp)) MainActionButton( title = "Share", - iconResourceId = CommonDrawables.ic_compound_share_android, + imageVector = CompoundIcons.ShareAndroid, onClick = { }, enabled = false, ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt index 1837041e64..78897efb50 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt @@ -32,12 +32,12 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun RetryDialog( content: String, + onRetry: () -> Unit, + onDismiss: () -> Unit, modifier: Modifier = Modifier, title: String = RetryDialogDefaults.title, retryText: String = RetryDialogDefaults.retryText, dismissText: String = RetryDialogDefaults.dismissText, - onRetry: () -> Unit = {}, - onDismiss: () -> Unit = {}, ) { BasicAlertDialog(modifier = modifier, onDismissRequest = onDismiss) { RetryDialogContent( @@ -54,12 +54,12 @@ fun RetryDialog( @Composable private fun RetryDialogContent( content: String, + onRetry: () -> Unit, + onDismiss: () -> Unit, modifier: Modifier = Modifier, title: String = RetryDialogDefaults.title, retryText: String = RetryDialogDefaults.retryText, dismissText: String = RetryDialogDefaults.dismissText, - onRetry: () -> Unit = {}, - onDismiss: () -> Unit = {}, ) { SimpleAlertDialogContent( modifier = modifier, @@ -85,6 +85,8 @@ internal fun RetryDialogPreview() { DialogPreview { RetryDialogContent( content = "Content", + onRetry = {}, + onDismiss = {}, ) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/TextFieldListItem.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/TextFieldListItem.kt index 93268e25d7..c26c12c7ea 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/TextFieldListItem.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/TextFieldListItem.kt @@ -28,7 +28,7 @@ import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.theme.components.OutlinedTextField import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable fun TextFieldListItem( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/media/WaveformPlaybackView.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/media/WaveformPlaybackView.kt index bae772c0f2..6921c7f809 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/media/WaveformPlaybackView.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/media/WaveformPlaybackView.kt @@ -45,7 +45,7 @@ import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList @@ -59,9 +59,9 @@ private const val DEFAULT_GRAPHICS_LAYER_ALPHA: Float = 0.99F * @param playbackProgress The current playback progress, between 0 and 1. * @param showCursor Whether to show the cursor or not. * @param waveform The waveform to display. Use [FakeWaveformFactory] to generate a fake waveform. + * @param onSeek Callback when the user seeks the waveform. Called with a value between 0 and 1. * @param modifier The modifier to be applied to the view. * @param seekEnabled Whether the user can seek the waveform or not. - * @param onSeek Callback when the user seeks the waveform. Called with a value between 0 and 1. * @param brush The brush to use to draw the waveform. * @param progressBrush The brush to use to draw the progress. * @param cursorBrush The brush to use to draw the cursor. @@ -74,9 +74,9 @@ fun WaveformPlaybackView( playbackProgress: Float, showCursor: Boolean, waveform: ImmutableList, + onSeek: (progress: Float) -> Unit, modifier: Modifier = Modifier, seekEnabled: Boolean = true, - onSeek: (progress: Float) -> Unit = {}, brush: Brush = SolidColor(ElementTheme.colors.iconQuaternary), progressBrush: Brush = SolidColor(ElementTheme.colors.iconSecondary), cursorBrush: Brush = SolidColor(ElementTheme.colors.iconAccentTertiary), @@ -183,18 +183,21 @@ internal fun WaveformPlaybackViewPreview() = ElementPreview { modifier = Modifier.height(34.dp), showCursor = false, playbackProgress = 0.5f, + onSeek = {}, waveform = persistentListOf(), ) WaveformPlaybackView( modifier = Modifier.height(34.dp), showCursor = false, playbackProgress = 0.5f, + onSeek = {}, waveform = persistentListOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f, 9f, 8f, 7f, 6f, 5f, 4f, 3f, 2f, 1f, 0f), ) WaveformPlaybackView( modifier = Modifier.height(34.dp), showCursor = true, playbackProgress = 0.5f, + onSeek = {}, waveform = List(1024) { it / 1024f }.toPersistentList(), ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCategory.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCategory.kt index e3036194a4..03631634a6 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCategory.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCategory.kt @@ -27,8 +27,8 @@ import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.designsystem.icons.CompoundDrawables @Composable fun PreferenceCategory( @@ -77,18 +77,20 @@ private fun ContentToPreview() { ) { PreferenceText( title = "Title", - iconResourceId = CommonDrawables.ic_compound_chat_problem, + iconResourceId = CompoundDrawables.ic_chat_problem, ) PreferenceSwitch( title = "Switch", - iconResourceId = CommonDrawables.ic_compound_threads, - isChecked = true + iconResourceId = CompoundDrawables.ic_threads, + isChecked = true, + onCheckedChange = {}, ) PreferenceSlide( title = "Slide", summary = "Summary", value = 0.75F, showIconAreaIfNoIcon = true, + onValueChange = {}, ) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCheckbox.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCheckbox.kt index b44b3ef2ad..68a29c9795 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCheckbox.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCheckbox.kt @@ -30,27 +30,27 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.components.preferences.components.PreferenceIcon +import io.element.android.libraries.designsystem.icons.CompoundDrawables import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.theme.components.Checkbox import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.toEnabledColor import io.element.android.libraries.designsystem.toSecondaryEnabledColor -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme @Composable fun PreferenceCheckbox( title: String, isChecked: Boolean, + onCheckedChange: (Boolean) -> Unit, modifier: Modifier = Modifier, supportingText: String? = null, enabled: Boolean = true, icon: ImageVector? = null, @DrawableRes iconResourceId: Int? = null, showIconAreaIfNoIcon: Boolean = false, - onCheckedChange: (Boolean) -> Unit = {}, ) { Row( modifier = modifier @@ -102,16 +102,18 @@ private fun ContentToPreview() { Column { PreferenceCheckbox( title = "Checkbox", - iconResourceId = CommonDrawables.ic_compound_threads, + iconResourceId = CompoundDrawables.ic_threads, enabled = true, - isChecked = true + isChecked = true, + onCheckedChange = {}, ) PreferenceCheckbox( title = "Checkbox with supporting text", supportingText = "Supporting text", - iconResourceId = CommonDrawables.ic_compound_threads, + iconResourceId = CompoundDrawables.ic_threads, enabled = true, - isChecked = true + isChecked = true, + onCheckedChange = {}, ) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDivider.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDivider.kt index 348a12bdd9..1727c68ca5 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDivider.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDivider.kt @@ -22,7 +22,7 @@ import androidx.compose.ui.tooling.preview.Preview import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.theme.components.HorizontalDivider -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable fun PreferenceDivider( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferencePage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferencePage.kt index 653df4a74d..66c99fda45 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferencePage.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferencePage.kt @@ -18,7 +18,6 @@ package io.element.android.libraries.designsystem.components.preferences import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxSize @@ -32,22 +31,21 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.components.button.BackButton -import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.icons.CompoundDrawables import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme -@OptIn(ExperimentalLayoutApi::class) @Composable fun PreferencePage( title: String, + onBackPressed: () -> Unit, modifier: Modifier = Modifier, - onBackPressed: () -> Unit = {}, snackbarHost: @Composable () -> Unit = {}, content: @Composable ColumnScope.() -> Unit, ) { @@ -81,8 +79,8 @@ fun PreferencePage( @Composable private fun PreferenceTopAppBar( title: String, + onBackPressed: () -> Unit, modifier: Modifier = Modifier, - onBackPressed: () -> Unit = {}, ) { TopAppBar( modifier = modifier, @@ -105,7 +103,8 @@ private fun PreferenceTopAppBar( @Composable internal fun PreferenceViewPreview() = ElementPreview { PreferencePage( - title = "Preference screen" + title = "Preference screen", + onBackPressed = {}, ) { PreferenceCategory( title = "Category title", @@ -113,19 +112,21 @@ internal fun PreferenceViewPreview() = ElementPreview { PreferenceText( title = "Title", subtitle = "Some other text", - iconResourceId = CommonDrawables.ic_compound_chat_problem, + iconResourceId = CompoundDrawables.ic_chat_problem, ) PreferenceDivider() PreferenceSwitch( title = "Switch", - iconResourceId = CommonDrawables.ic_compound_threads, + iconResourceId = CompoundDrawables.ic_threads, isChecked = true, + onCheckedChange = {}, ) PreferenceDivider() PreferenceCheckbox( title = "Checkbox", - iconResourceId = CommonDrawables.ic_compound_notifications, + iconResourceId = CompoundDrawables.ic_notifications, isChecked = true, + onCheckedChange = {}, ) PreferenceDivider() PreferenceSlide( @@ -133,6 +134,7 @@ internal fun PreferenceViewPreview() = ElementPreview { summary = "Summary", value = 0.75F, showIconAreaIfNoIcon = true, + onValueChange = {}, ) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSlide.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSlide.kt index 154722d440..4b125480f7 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSlide.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSlide.kt @@ -28,20 +28,21 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.components.preferences.components.PreferenceIcon +import io.element.android.libraries.designsystem.icons.CompoundDrawables import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.theme.components.Slider import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.toEnabledColor -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme @Composable fun PreferenceSlide( title: String, @FloatRange(0.0, 1.0) value: Float, + onValueChange: (Float) -> Unit, modifier: Modifier = Modifier, icon: ImageVector? = null, @DrawableRes iconResourceId: Int? = null, @@ -49,7 +50,6 @@ fun PreferenceSlide( enabled: Boolean = true, summary: String? = null, steps: Int = 0, - onValueChange: (Float) -> Unit = {}, ) { Row( modifier = modifier @@ -95,9 +95,10 @@ internal fun PreferenceSlidePreview() = ElementThemedPreview { ContentToPreview( @Composable private fun ContentToPreview() { PreferenceSlide( - iconResourceId = CommonDrawables.ic_compound_user_profile, + iconResourceId = CompoundDrawables.ic_user_profile, title = "Slide", summary = "Summary", - value = 0.75F + value = 0.75F, + onValueChange = {}, ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSwitch.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSwitch.kt index 07c3f67270..3d0ceb87c2 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSwitch.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSwitch.kt @@ -32,27 +32,27 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.components.preferences.components.PreferenceIcon +import io.element.android.libraries.designsystem.icons.CompoundDrawables import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.theme.components.Switch import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.toEnabledColor import io.element.android.libraries.designsystem.toSecondaryEnabledColor -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme @Composable fun PreferenceSwitch( title: String, isChecked: Boolean, + onCheckedChange: (Boolean) -> Unit, modifier: Modifier = Modifier, subtitle: String? = null, enabled: Boolean = true, icon: ImageVector? = null, @DrawableRes iconResourceId: Int? = null, showIconAreaIfNoIcon: Boolean = false, - onCheckedChange: (Boolean) -> Unit = {}, switchAlignment: Alignment.Vertical = Alignment.CenterVertically ) { Row( @@ -109,8 +109,9 @@ private fun ContentToPreview() { PreferenceSwitch( title = "Switch", subtitle = "Subtitle Switch", - iconResourceId = CommonDrawables.ic_compound_threads, + iconResourceId = CompoundDrawables.ic_threads, enabled = true, - isChecked = true + isChecked = true, + onCheckedChange = {}, ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceText.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceText.kt index 20146e6e24..c006f0c6ef 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceText.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceText.kt @@ -34,8 +34,10 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.atomic.atoms.RedIndicatorAtom import io.element.android.libraries.designsystem.components.preferences.components.PreferenceIcon +import io.element.android.libraries.designsystem.icons.CompoundDrawables import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.PreviewGroup @@ -43,8 +45,6 @@ import io.element.android.libraries.designsystem.theme.components.CircularProgre import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.toEnabledColor import io.element.android.libraries.designsystem.toSecondaryEnabledColor -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme /** * Tried to use ListItem, but it cannot really match the design. Keep custom Layout for now. @@ -169,26 +169,26 @@ private fun ContentToPreview(showEndBadge: Boolean) { ) { PreferenceText( title = "Title", - iconResourceId = CommonDrawables.ic_compound_chat_problem, + iconResourceId = CompoundDrawables.ic_chat_problem, showEndBadge = showEndBadge, ) PreferenceText( title = "Title", subtitle = "Some content", - iconResourceId = CommonDrawables.ic_compound_chat_problem, + iconResourceId = CompoundDrawables.ic_chat_problem, showEndBadge = showEndBadge, ) PreferenceText( title = "Title", subtitle = "Some content", - iconResourceId = CommonDrawables.ic_compound_chat_problem, + iconResourceId = CompoundDrawables.ic_chat_problem, currentValue = "123", showEndBadge = showEndBadge, ) PreferenceText( title = "Title", subtitle = "Some content", - iconResourceId = CommonDrawables.ic_compound_chat_problem, + iconResourceId = CompoundDrawables.ic_chat_problem, currentValue = "123", enabled = false, showEndBadge = showEndBadge, @@ -196,19 +196,19 @@ private fun ContentToPreview(showEndBadge: Boolean) { PreferenceText( title = "Title", subtitle = "Some content", - iconResourceId = CommonDrawables.ic_compound_chat_problem, + iconResourceId = CompoundDrawables.ic_chat_problem, loadingCurrentValue = true, showEndBadge = showEndBadge, ) PreferenceText( title = "Title", - iconResourceId = CommonDrawables.ic_compound_chat_problem, + iconResourceId = CompoundDrawables.ic_chat_problem, currentValue = "123", showEndBadge = showEndBadge, ) PreferenceText( title = "Title", - iconResourceId = CommonDrawables.ic_compound_chat_problem, + iconResourceId = CompoundDrawables.ic_chat_problem, loadingCurrentValue = true, showEndBadge = showEndBadge, ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/PreferenceIcon.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/PreferenceIcon.kt index c29b46f3c3..61e8aabdd7 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/PreferenceIcon.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/PreferenceIcon.kt @@ -51,7 +51,7 @@ fun PreferenceIcon( Icon( imageVector = icon, resourceId = iconResourceId, - contentDescription = "", + contentDescription = null, tint = tintColor ?: enabled.toSecondaryEnabledColor(), modifier = Modifier .padding(end = 16.dp) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/tooltip/PlainTooltip.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/tooltip/PlainTooltip.kt index 78f381a685..b61aa3dda2 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/tooltip/PlainTooltip.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/tooltip/PlainTooltip.kt @@ -22,7 +22,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import androidx.compose.material3.PlainTooltip as M3PlainTooltip @OptIn(ExperimentalMaterial3Api::class) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/VerificationEmoji.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/CompoundDrawables.kt similarity index 81% rename from libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/VerificationEmoji.kt rename to libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/CompoundDrawables.kt index 43b89b6587..2cac0b0596 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/VerificationEmoji.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/CompoundDrawables.kt @@ -14,9 +14,6 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.api.verification +package io.element.android.libraries.designsystem.icons -data class VerificationEmoji( - val code: String, - val name: String, -) +typealias CompoundDrawables = io.element.android.compound.R.drawable diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsList.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsList.kt index 8dee3921ac..cd7e36d611 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsList.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsList.kt @@ -18,84 +18,6 @@ package io.element.android.libraries.designsystem.icons import io.element.android.libraries.designsystem.R -internal val iconsCompound = listOf( - R.drawable.ic_compound_arrow_left, - R.drawable.ic_compound_arrow_right, - R.drawable.ic_compound_arrow_up_right, - R.drawable.ic_compound_block, - R.drawable.ic_compound_chat, - R.drawable.ic_compound_chat_new, - R.drawable.ic_compound_chat_problem, - R.drawable.ic_compound_check, - R.drawable.ic_compound_check_circle, - R.drawable.ic_compound_check_circle_solid, - R.drawable.ic_compound_chevron_down, - R.drawable.ic_compound_chevron_left, - R.drawable.ic_compound_chevron_right, - R.drawable.ic_compound_chevron_up_down, - R.drawable.ic_compound_close, - R.drawable.ic_compound_computer, - R.drawable.ic_compound_delete, - R.drawable.ic_compound_download, - R.drawable.ic_compound_drag_grid, - R.drawable.ic_compound_drag_list, - R.drawable.ic_compound_end_call, - R.drawable.ic_compound_error, - R.drawable.ic_compound_export_archive, - R.drawable.ic_compound_extensions, - R.drawable.ic_compound_favourite_off, - R.drawable.ic_compound_favourite_on, - R.drawable.ic_compound_files, - R.drawable.ic_compound_filter, - R.drawable.ic_compound_grid_view, - R.drawable.ic_compound_info, - R.drawable.ic_compound_info_solid, - R.drawable.ic_compound_leave, - R.drawable.ic_compound_link, - R.drawable.ic_compound_lock, - R.drawable.ic_compound_lock_off, - R.drawable.ic_compound_marker_read_receipts, - R.drawable.ic_compound_mention, - R.drawable.ic_compound_mic_off_outline, - R.drawable.ic_compound_mic_off_solid, - R.drawable.ic_compound_mic_on_outline, - R.drawable.ic_compound_mic_on_solid, - R.drawable.ic_compound_mobile, - R.drawable.ic_compound_notifications, - R.drawable.ic_compound_notifications_off, - R.drawable.ic_compound_notifications_solid, - R.drawable.ic_compound_notifications_solid_off, - R.drawable.ic_compound_offline, - R.drawable.ic_compound_overflow_horizontal, - R.drawable.ic_compound_overflow_vertical, - R.drawable.ic_compound_polls, - R.drawable.ic_compound_pop_out, - R.drawable.ic_compound_public, - R.drawable.ic_compound_search, - R.drawable.ic_compound_settings, - R.drawable.ic_compound_settings_solid, - R.drawable.ic_compound_share, - R.drawable.ic_compound_share_android, - R.drawable.ic_compound_share_screen_outline, - R.drawable.ic_compound_share_screen_solid, - R.drawable.ic_compound_spotlight_view, - R.drawable.ic_compound_threads, - R.drawable.ic_compound_threads_solid, - R.drawable.ic_compound_user, - R.drawable.ic_compound_user_add, - R.drawable.ic_compound_user_add_solid, - R.drawable.ic_compound_user_profile, - R.drawable.ic_compound_verified, - R.drawable.ic_compound_video_call, - R.drawable.ic_compound_video_call_declined, - R.drawable.ic_compound_video_call_missed, - R.drawable.ic_compound_video_call_off, - R.drawable.ic_compound_visibility_off, - R.drawable.ic_compound_visibility_on, - R.drawable.ic_compound_voice_call, - R.drawable.ic_compound_web_browser, -) - // This list and all the drawable it contains should be removed at some point. // All the icons should be defined in Compound. internal val iconsOther = listOf( @@ -106,7 +28,6 @@ internal val iconsOther = listOf( R.drawable.ic_devices, R.drawable.ic_edit, R.drawable.ic_edit_outline, - R.drawable.ic_edit_solid, R.drawable.ic_encryption_enabled, R.drawable.ic_forward, R.drawable.ic_groups, @@ -121,18 +42,15 @@ internal val iconsOther = listOf( R.drawable.ic_new_message, R.drawable.ic_numbered_list, R.drawable.ic_plus, - R.drawable.ic_poll_end, R.drawable.ic_quote, R.drawable.ic_reply, - R.drawable.ic_send, + R.drawable.ic_retry, R.drawable.ic_sign_out, R.drawable.ic_strikethrough, R.drawable.ic_take_photo_camera, R.drawable.ic_text_formatting, - R.drawable.ic_thread_decoration, R.drawable.ic_underline, R.drawable.ic_user, R.drawable.ic_user_add, - R.drawable.ic_video_call, R.drawable.ic_waiting_to_decrypt, ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt index 1e67f097e4..a131410646 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt @@ -30,20 +30,21 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toPersistentList internal class CompoundIconListPreviewProvider : PreviewParameterProvider { override val values: Sequence get() { - val chunks = iconsCompound.chunked(36) + val chunks = CompoundIcons.allResIds.chunked(36) return chunks.mapIndexed { index, chunk -> - IconChunk(index = index+1, total = chunks.size, icons = chunk.toPersistentList()) + IconChunk(index = index + 1, total = chunks.size, icons = chunk.toPersistentList()) } .asSequence() } @@ -109,7 +110,7 @@ private fun IconsPreview( textAlign = TextAlign.Center, ) iconsList.chunked(6).forEach { iconsRow -> - Row(horizontalArrangement = Arrangement.spacedBy(1.dp)) { + Row(horizontalArrangement = Arrangement.spacedBy(2.dp)) { iconsRow.forEach { icon -> Column( modifier = Modifier.width(48.dp), diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementPreview.kt index 70777e0184..a7fba965dc 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementPreview.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementPreview.kt @@ -19,7 +19,7 @@ package io.element.android.libraries.designsystem.preview import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.Composable import io.element.android.libraries.designsystem.theme.components.Surface -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable @Suppress("ModifierMissing") diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/AnnotatedStrings.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/AnnotatedStrings.kt index 04a872a1cf..10cd56be64 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/AnnotatedStrings.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/AnnotatedStrings.kt @@ -31,7 +31,7 @@ import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextDecoration -import io.element.android.libraries.theme.LinkColor +import io.element.android.compound.theme.LinkColor fun String.toAnnotatedString(): AnnotatedString = buildAnnotatedString { append(this@toAnnotatedString) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt index 95cf050cc3..a593e81f7b 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt @@ -28,7 +28,7 @@ import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.WithFontScale import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme /** * Return the maximum value between the receiver value and the value with fontScale applied. diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/TextSyleToTypeface.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/TextSyleToTypeface.kt new file mode 100644 index 0000000000..aa445a6c99 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/TextSyleToTypeface.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.designsystem.text + +import android.graphics.Typeface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalFontFamilyResolver +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontSynthesis +import androidx.compose.ui.text.font.FontWeight + +@Composable +fun TextStyle.rememberTypeface(): State { + val resolver: FontFamily.Resolver = LocalFontFamilyResolver.current + @Suppress("UNCHECKED_CAST") + return remember(resolver, this) { + resolver.resolve( + fontFamily = fontFamily, + fontWeight = fontWeight ?: FontWeight.Normal, + fontStyle = fontStyle ?: FontStyle.Normal, + fontSynthesis = fontSynthesis ?: FontSynthesis.All, + ) + } as State +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt index c860239d6a..d81484d512 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt @@ -19,11 +19,11 @@ package io.element.android.libraries.designsystem.theme import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color -import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.compound.previews.ColorListPreview +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.SemanticColors import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.theme.ElementTheme -import io.element.android.libraries.theme.compound.generated.SemanticColors -import io.element.android.libraries.theme.previews.ColorListPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import kotlinx.collections.immutable.persistentMapOf /** diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/TypographyAliases.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/TypographyAliases.kt index f5aba325d8..03359a8349 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/TypographyAliases.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/TypographyAliases.kt @@ -17,7 +17,7 @@ package io.element.android.libraries.designsystem.theme import androidx.compose.ui.text.TextStyle -import io.element.android.libraries.theme.compound.generated.TypographyTokens +import io.element.android.compound.tokens.generated.TypographyTokens /* * This file contains aliases for TypographyTokens. diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt index c41e7b868d..70a906f758 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt @@ -40,12 +40,12 @@ import androidx.compose.ui.layout.Placeable import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag -import io.element.android.libraries.theme.ElementTheme import kotlin.math.max // Figma designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=911%3A343492&mode=design&t=jeyd1bXKOOx8y10r-1 @@ -439,7 +439,7 @@ internal fun DialogWithTitleIconAndOkButtonPreview() { SimpleAlertDialogContent( icon = { Icon( - resourceId = CommonDrawables.ic_compound_notifications_solid, + imageVector = CompoundIcons.NotificationsSolid, contentDescription = null ) }, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/BottomSheetDragHandle.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/BottomSheetDragHandle.kt index 63dcf1b653..da1bf0e4fe 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/BottomSheetDragHandle.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/BottomSheetDragHandle.kt @@ -35,7 +35,7 @@ import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable fun BottomSheetDragHandle( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt index 30734f3f12..fe0d807745 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt @@ -47,14 +47,14 @@ import androidx.compose.ui.graphics.isSpecified import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.rememberVectorPainter -import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme // Designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&mode=design&t=U03tOFZz5FSLVUMa-1 @@ -248,7 +248,7 @@ sealed interface IconSource { @Composable fun getPainter(): Painter = when (this) { - is Resource -> painterResource(id) + is Resource -> rememberVectorPainter(image = ImageVector.vectorResource(id)) is Vector -> rememberVectorPainter(image = vector) } } @@ -395,7 +395,7 @@ private fun ColumnScope.ButtonMatrixPreview( ) // With icon ButtonRowPreview( - leadingIcon = IconSource.Resource(CommonDrawables.ic_compound_share_android), + leadingIcon = IconSource.Vector(CompoundIcons.ShareAndroid), style = style, size = size, destructive = destructive, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Checkbox.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Checkbox.kt index 31c6cc3647..95cf0b412e 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Checkbox.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Checkbox.kt @@ -33,7 +33,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme // Designs in https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&mode=design&t=qb99xBP5mwwCtGkN-1 diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenu.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenu.kt index c0160bb7f8..fe6c27254a 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenu.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenu.kt @@ -24,7 +24,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.window.PopupProperties -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme // Figma designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=1032%3A44063&mode=design&t=rsNegTbEVLYAXL76-1 diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenuItem.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenuItem.kt index 7dc48e40fd..3e49018166 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenuItem.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenuItem.kt @@ -28,10 +28,10 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme // Figma designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=1032%3A44063&mode=design&t=rsNegTbEVLYAXL76-1 @@ -86,32 +86,32 @@ private fun ContentToPreview() { DropdownMenuItem( text = { Text(text = "Item") }, onClick = {}, - trailingIcon = { Icon(resourceId = CommonDrawables.ic_compound_chevron_right, contentDescription = null) }, + trailingIcon = { Icon(imageVector = CompoundIcons.ChevronRight, contentDescription = null) }, ) HorizontalDivider() DropdownMenuItem( text = { Text(text = "Item") }, onClick = {}, - leadingIcon = { Icon(resourceId = CommonDrawables.ic_compound_chat_problem, contentDescription = null) }, + leadingIcon = { Icon(imageVector = CompoundIcons.ChatProblem, contentDescription = null) }, ) DropdownMenuItem( text = { Text(text = "Item") }, onClick = {}, - leadingIcon = { Icon(resourceId = CommonDrawables.ic_compound_chat_problem, contentDescription = null) }, - trailingIcon = { Icon(resourceId = CommonDrawables.ic_compound_chevron_right, contentDescription = null) }, + leadingIcon = { Icon(imageVector = CompoundIcons.ChatProblem, contentDescription = null) }, + trailingIcon = { Icon(imageVector = CompoundIcons.ChevronRight, contentDescription = null) }, ) DropdownMenuItem( text = { Text(text = "Item") }, onClick = {}, enabled = false, - leadingIcon = { Icon(resourceId = CommonDrawables.ic_compound_chat_problem, contentDescription = null) }, - trailingIcon = { Icon(resourceId = CommonDrawables.ic_compound_chevron_right, contentDescription = null) }, + leadingIcon = { Icon(imageVector = CompoundIcons.ChatProblem, contentDescription = null) }, + trailingIcon = { Icon(imageVector = CompoundIcons.ChevronRight, contentDescription = null) }, ) HorizontalDivider() DropdownMenuItem( text = { Text(text = "Multiline\nItem") }, onClick = {}, - trailingIcon = { Icon(resourceId = CommonDrawables.ic_compound_chevron_right, contentDescription = null) }, + trailingIcon = { Icon(imageVector = CompoundIcons.ChevronRight, contentDescription = null) }, ) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/FloatingActionButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/FloatingActionButton.kt index ed32018f84..9a01cfe69c 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/FloatingActionButton.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/FloatingActionButton.kt @@ -30,9 +30,9 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.utils.CommonDrawables @Composable fun FloatingActionButton( @@ -66,7 +66,7 @@ internal fun FloatingActionButtonPreview() = private fun ContentToPreview() { Box(modifier = Modifier.padding(8.dp)) { FloatingActionButton(onClick = {}) { - Icon(resourceId = CommonDrawables.ic_compound_close, contentDescription = "") + Icon(imageVector = CompoundIcons.Close, contentDescription = null) } } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Icon.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Icon.kt index 1813db36f2..fb36f5e3e6 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Icon.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Icon.kt @@ -24,11 +24,11 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.utils.CommonDrawables /** * Icon is a wrapper around [androidx.compose.material3.Icon] which allows to use @@ -116,7 +116,7 @@ fun Icon( tint: Color = LocalContentColor.current, ) { androidx.compose.material3.Icon( - painter = painterResource(id = resourceId), + imageVector = ImageVector.vectorResource(id = resourceId), contentDescription = contentDescription, modifier = modifier, tint = tint @@ -145,5 +145,5 @@ internal fun IconImageVectorPreview() = @Composable private fun ContentToPreview() { - Icon(resourceId = CommonDrawables.ic_compound_close, contentDescription = "") + Icon(imageVector = CompoundIcons.Close, contentDescription = null) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconButton.kt index fd6d2ed471..f0a9c12ac0 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconButton.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconButton.kt @@ -27,10 +27,10 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme // Figma designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=1182%3A48861&mode=design&t=Shlcvznm1oUyqGC2-1 @@ -67,20 +67,20 @@ private fun ContentToPreview() { CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.iconPrimary) { Row { IconButton(onClick = {}) { - Icon(resourceId = CommonDrawables.ic_compound_close, contentDescription = "") + Icon(imageVector = CompoundIcons.Close, contentDescription = null) } IconButton(enabled = false, onClick = {}) { - Icon(resourceId = CommonDrawables.ic_compound_close, contentDescription = "") + Icon(imageVector = CompoundIcons.Close, contentDescription = null) } } } CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.iconSecondary) { Row { IconButton(onClick = {}) { - Icon(resourceId = CommonDrawables.ic_compound_close, contentDescription = "") + Icon(imageVector = CompoundIcons.Close, contentDescription = null) } IconButton(enabled = false, onClick = {}) { - Icon(resourceId = CommonDrawables.ic_compound_close, contentDescription = "") + Icon(imageVector = CompoundIcons.Close, contentDescription = null) } } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconToggleButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconToggleButton.kt index fd355d3860..92bc5c7605 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconToggleButton.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconToggleButton.kt @@ -69,7 +69,7 @@ private fun ContentToPreview() { val icon: @Composable () -> Unit = { Icon( imageVector = if (checked) Icons.Default.CheckCircle else Icons.Default.RadioButtonUnchecked, - contentDescription = "IconToggleButton" + contentDescription = null ) } IconToggleButton(checked = checked, enabled = true, onCheckedChange = { checked = !checked }, content = icon) @@ -79,7 +79,7 @@ private fun ContentToPreview() { val icon: @Composable () -> Unit = { Icon( imageVector = if (!checked) Icons.Default.CheckCircle else Icons.Default.RadioButtonUnchecked, - contentDescription = "IconToggleButton" + contentDescription = null ) } IconToggleButton(checked = !checked, enabled = true, onCheckedChange = { checked = !checked }, content = icon) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListItem.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListItem.kt index 0b0584da98..ebb8358321 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListItem.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListItem.kt @@ -33,11 +33,11 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme // Designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24208&mode=design&t=G5hCfkLB6GgXDuWe-1 @@ -466,7 +466,8 @@ private object PreviewItems { return ListItemContent.Switch(checked = checked, onChange = { checked = !checked }) } + @Composable fun icon() = ListItemContent.Icon( - iconSource = IconSource.Resource(CommonDrawables.ic_compound_share_android) + iconSource = IconSource.Vector(CompoundIcons.ShareAndroid) ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSectionHeader.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSectionHeader.kt index 646c7cc6dd..4ad6a3c48d 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSectionHeader.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSectionHeader.kt @@ -29,7 +29,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme // Designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24208&mode=design&t=G5hCfkLB6GgXDuWe-1 diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSupportingText.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSupportingText.kt index d71073b604..e6305d4dc2 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSupportingText.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSupportingText.kt @@ -27,12 +27,12 @@ import androidx.compose.ui.text.ExperimentalTextApi import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.ClickableLinkText import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme // Designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24208&mode=design&t=G5hCfkLB6GgXDuWe-1 @@ -159,7 +159,7 @@ internal fun ListSupportingTextSmallPaddingPreview() { Column { ListItem( headlineContent = { Text("A title") }, - leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_share_android)) + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.ShareAndroid)) ) ListSupportingText( text = "Supporting line text lorem ipsum dolor sit amet, consectetur. Read more", diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt index 017e6ed3e7..754c5dde9c 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt @@ -27,11 +27,11 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -74,7 +74,7 @@ private fun ContentToPreview() { TextButton(text = "Action", onClick = {}) IconButton(onClick = {}) { Icon( - resourceId = CommonDrawables.ic_compound_share_android, + imageVector = CompoundIcons.ShareAndroid, contentDescription = null, ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt index 03e5c06eef..234a0e0b60 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt @@ -38,7 +38,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.sheetStateForPreview -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/RadioButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/RadioButton.kt index 9f373d1b71..038e74d982 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/RadioButton.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/RadioButton.kt @@ -32,7 +32,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme // Designs in https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24202&mode=design&t=qb99xBP5mwwCtGkN-1 diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SearchBar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SearchBar.kt index 19b73bf46a..7e208b88d9 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SearchBar.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SearchBar.kt @@ -41,11 +41,11 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @OptIn(ExperimentalMaterial3Api::class) @@ -98,7 +98,7 @@ fun SearchBar( { IconButton(onClick = { onQueryChange("") }) { Icon( - resourceId = CommonDrawables.ic_compound_close, + imageVector = CompoundIcons.Close, contentDescription = stringResource(CommonStrings.action_clear), ) } @@ -108,7 +108,7 @@ fun SearchBar( !active -> { { Icon( - resourceId = CommonDrawables.ic_compound_search, + imageVector = CompoundIcons.Search, contentDescription = stringResource(CommonStrings.action_search), tint = MaterialTheme.colorScheme.tertiary, ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SegmentedButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SegmentedButton.kt new file mode 100644 index 0000000000..1c37cbce1e --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SegmentedButton.kt @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.designsystem.theme.components + +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.SegmentedButton +import androidx.compose.material3.SegmentedButtonDefaults +import androidx.compose.material3.SingleChoiceSegmentedButtonRowScope +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import io.element.android.compound.theme.ElementTheme + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SingleChoiceSegmentedButtonRowScope.SegmentedButton( + index: Int, + count: Int, + selected: Boolean, + onClick: () -> Unit, + text: String, + modifier: Modifier = Modifier, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + enabled: Boolean = true, +) { + SegmentedButton( + selected = selected, + onClick = onClick, + modifier = modifier, + interactionSource = interactionSource, + enabled = enabled, + shape = SegmentedButtonDefaults.itemShape(index = index, count = count), + label = { + Text( + text = text, + style = ElementTheme.typography.fontBodyMdMedium, + ) + }, + colors = SegmentedButtonDefaults.colors( + activeContainerColor = ElementTheme.materialColors.primary, + activeContentColor = ElementTheme.materialColors.onPrimary, + activeBorderColor = ElementTheme.materialColors.primary, + inactiveContainerColor = ElementTheme.materialColors.surface, + inactiveContentColor = ElementTheme.materialColors.onSurface, + inactiveBorderColor = ElementTheme.materialColors.primary, + disabledActiveContainerColor = ElementTheme.colors.bgActionPrimaryDisabled, + disabledActiveContentColor = ElementTheme.colors.textOnSolidPrimary, + disabledActiveBorderColor = ElementTheme.colors.bgActionPrimaryDisabled, + disabledInactiveContainerColor = ElementTheme.materialColors.surface, + disabledInactiveContentColor = ElementTheme.colors.textDisabled, + disabledInactiveBorderColor = Color.Transparent, + ) + ) +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Snackbar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Snackbar.kt index d610144198..8a2990a3b4 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Snackbar.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Snackbar.kt @@ -24,13 +24,13 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.theme.SnackBarLabelColorDark +import io.element.android.compound.theme.SnackBarLabelColorLight +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.button.ButtonVisuals import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme -import io.element.android.libraries.theme.SnackBarLabelColorDark -import io.element.android.libraries.theme.SnackBarLabelColorLight @Composable fun Snackbar( @@ -120,7 +120,7 @@ internal fun SnackbarWithActionAndCloseButtonPreview() { message = "Snackbar supporting text", action = ButtonVisuals.Text("Action") {}, dismissAction = ButtonVisuals.Icon( - IconSource.Resource(CommonDrawables.ic_compound_close) + IconSource.Vector(CompoundIcons.Close) ) {} ) } @@ -142,7 +142,7 @@ internal fun SnackbarWithActionOnNewLineAndCloseButtonPreview() { message = "Snackbar supporting text", action = ButtonVisuals.Text("Action", {}), dismissAction = ButtonVisuals.Icon( - IconSource.Resource(CommonDrawables.ic_compound_close) + IconSource.Vector(CompoundIcons.Close) ) {}, actionOnNewLine = true ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Switch.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Switch.kt index ab4c9dee05..b11cf433e0 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Switch.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Switch.kt @@ -34,7 +34,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import androidx.compose.material3.Switch as Material3Switch // Designs in https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24203&mode=design&t=qb99xBP5mwwCtGkN-1 diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Text.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Text.kt index 967eb00989..21330d0726 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Text.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Text.kt @@ -41,10 +41,10 @@ import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import io.element.android.compound.utils.toHrf import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.theme.utils.toHrf import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.persistentMapOf diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt index 6fd38653f8..d8bf1d7016 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt @@ -27,11 +27,11 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -74,7 +74,7 @@ private fun ContentToPreview() { TextButton(text = "Action", onClick = {}) IconButton(onClick = {}) { Icon( - resourceId = CommonDrawables.ic_compound_share_android, + imageVector = CompoundIcons.ShareAndroid, contentDescription = null, ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt index 4bbb20b6a2..e120276a08 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt @@ -24,6 +24,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.tooling.preview.Preview +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.theme.components.Button @@ -31,7 +32,6 @@ import io.element.android.libraries.designsystem.theme.components.DropdownMenu import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables @Preview(group = PreviewGroup.Menus) @Composable @@ -43,7 +43,10 @@ internal fun MenuPreview() { for (i in 0..5) { val leadingIcon: @Composable (() -> Unit)? = if (i in 2..3) { @Composable { - Icon(Icons.Filled.Favorite, contentDescription = "Favorite") + Icon( + imageVector = Icons.Filled.Favorite, + contentDescription = null + ) } } else { null @@ -52,8 +55,8 @@ internal fun MenuPreview() { val trailingIcon: @Composable (() -> Unit)? = if (i in 3..4) { @Composable { Icon( - resourceId = CommonDrawables.ic_compound_chevron_right, - contentDescription = "Favorite", + imageVector = CompoundIcons.ChevronRight, + contentDescription = null, ) } } else { diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/snackbar/SnackbarHost.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/snackbar/SnackbarHost.kt index 257c23a2b7..a327d2f651 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/snackbar/SnackbarHost.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/snackbar/SnackbarHost.kt @@ -21,10 +21,10 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.button.ButtonVisuals import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.Snackbar -import io.element.android.libraries.designsystem.utils.CommonDrawables @Composable fun SnackbarHost(hostState: SnackbarHostState, modifier: Modifier = Modifier) { @@ -35,7 +35,7 @@ fun SnackbarHost(hostState: SnackbarHostState, modifier: Modifier = Modifier) { action = data.visuals.actionLabel?.let { ButtonVisuals.Text(it, data::performAction) }, dismissAction = if (data.visuals.withDismissAction) { ButtonVisuals.Icon( - IconSource.Resource(CommonDrawables.ic_compound_close), + IconSource.Vector(CompoundIcons.Close), data::dismiss ) } else null, diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_arrow_left.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_arrow_left.xml deleted file mode 100644 index 0f4a60e5a3..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_arrow_left.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_arrow_right.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_arrow_right.xml deleted file mode 100644 index 03062d4dea..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_arrow_right.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_arrow_up_right.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_arrow_up_right.xml deleted file mode 100644 index 31213ed168..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_arrow_up_right.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_block.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_block.xml deleted file mode 100644 index 1139ad2291..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_block.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_chat.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_chat.xml deleted file mode 100644 index 55e7efe70d..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_chat.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_chat_new.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_chat_new.xml deleted file mode 100644 index a1481a57d3..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_chat_new.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_chat_problem.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_chat_problem.xml deleted file mode 100644 index 4d16775c66..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_chat_problem.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_check.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_check.xml deleted file mode 100644 index 879d9a28d3..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_check.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_check_circle.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_check_circle.xml deleted file mode 100644 index e0b95e183b..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_check_circle.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_check_circle_solid.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_check_circle_solid.xml deleted file mode 100644 index 79c856d35a..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_check_circle_solid.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_down.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_down.xml deleted file mode 100644 index c3624052df..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_down.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_left.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_left.xml deleted file mode 100644 index fd6f26a330..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_left.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_right.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_right.xml deleted file mode 100644 index c45219234c..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_right.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_up_down.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_up_down.xml deleted file mode 100644 index eca37146ab..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_up_down.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_close.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_close.xml deleted file mode 100644 index 1256db427c..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_close.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_computer.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_computer.xml deleted file mode 100644 index c8b4009abc..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_computer.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_delete.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_delete.xml deleted file mode 100644 index 1170f16553..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_delete.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_download.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_download.xml deleted file mode 100644 index 38518f2404..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_download.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_drag_grid.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_drag_grid.xml deleted file mode 100644 index 60a5b8a7cd..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_drag_grid.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_drag_list.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_drag_list.xml deleted file mode 100644 index fb80dafae2..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_drag_list.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_end_call.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_end_call.xml deleted file mode 100644 index 93cd5c54fb..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_end_call.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_error.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_error.xml deleted file mode 100644 index 0b892cce9d..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_error.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_export_archive.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_export_archive.xml deleted file mode 100644 index 34796f0ce1..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_export_archive.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_extensions.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_extensions.xml deleted file mode 100644 index 18d6feeb6d..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_extensions.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_favourite_off.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_favourite_off.xml deleted file mode 100644 index 2ec4dc6559..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_favourite_off.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_favourite_on.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_favourite_on.xml deleted file mode 100644 index 8347b8671f..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_favourite_on.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_files.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_files.xml deleted file mode 100644 index 0b99de6492..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_files.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_filter.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_filter.xml deleted file mode 100644 index 6a63f1c1d7..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_filter.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_grid_view.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_grid_view.xml deleted file mode 100644 index 8361a792d1..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_grid_view.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_info.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_info.xml deleted file mode 100644 index 171edb5c9b..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_info.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_info_solid.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_info_solid.xml deleted file mode 100644 index fdb53d2db9..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_info_solid.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_leave.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_leave.xml deleted file mode 100644 index 43d240246c..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_leave.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_link.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_link.xml deleted file mode 100644 index 15c9ab6889..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_link.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_lock.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_lock.xml deleted file mode 100644 index 748dcadf32..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_lock.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_lock_off.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_lock_off.xml deleted file mode 100644 index a7f6096d33..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_lock_off.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_marker_read_receipts.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_marker_read_receipts.xml deleted file mode 100644 index 3f5b4e7bfa..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_marker_read_receipts.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_mention.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_mention.xml deleted file mode 100644 index f5771354e1..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_mention.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_mic_off_outline.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_mic_off_outline.xml deleted file mode 100644 index 6dc333ce8f..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_mic_off_outline.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_mic_off_solid.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_mic_off_solid.xml deleted file mode 100644 index 27cf5bb1e3..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_mic_off_solid.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_mic_on_outline.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_mic_on_outline.xml deleted file mode 100644 index 6a17adee8d..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_mic_on_outline.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_mic_on_solid.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_mic_on_solid.xml deleted file mode 100644 index 4fe8b3cb57..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_mic_on_solid.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_mobile.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_mobile.xml deleted file mode 100644 index f5648fb87b..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_mobile.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_notifications.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_notifications.xml deleted file mode 100644 index fae20f074c..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_notifications.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_notifications_off.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_notifications_off.xml deleted file mode 100644 index e43e4ba904..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_notifications_off.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_notifications_solid.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_notifications_solid.xml deleted file mode 100644 index d3dd76cea9..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_notifications_solid.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_notifications_solid_off.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_notifications_solid_off.xml deleted file mode 100644 index daf60279e3..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_notifications_solid_off.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_offline.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_offline.xml deleted file mode 100644 index 4e73fe617c..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_offline.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_overflow_horizontal.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_overflow_horizontal.xml deleted file mode 100644 index 4b8c1476fb..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_overflow_horizontal.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_overflow_vertical.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_overflow_vertical.xml deleted file mode 100644 index 242846cee8..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_overflow_vertical.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_plus.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_plus.xml deleted file mode 100644 index a56299f569..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_plus.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_polls.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_polls.xml deleted file mode 100644 index 1fda4c4dea..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_polls.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_pop_out.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_pop_out.xml deleted file mode 100644 index fef7a15d5c..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_pop_out.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_public.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_public.xml deleted file mode 100644 index 79831bb75e..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_public.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_search.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_search.xml deleted file mode 100644 index 31f8a6a5ef..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_search.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_settings.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_settings.xml deleted file mode 100644 index b15d9990c0..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_settings.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_settings_solid.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_settings_solid.xml deleted file mode 100644 index 0e6071126b..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_settings_solid.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_share.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_share.xml deleted file mode 100644 index cf17225881..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_share.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_share_android.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_share_android.xml deleted file mode 100644 index 8ea3ee523a..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_share_android.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_share_screen_outline.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_share_screen_outline.xml deleted file mode 100644 index af750bf6fd..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_share_screen_outline.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_share_screen_solid.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_share_screen_solid.xml deleted file mode 100644 index a5608dde2f..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_share_screen_solid.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_spotlight_view.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_spotlight_view.xml deleted file mode 100644 index db8133f1b6..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_spotlight_view.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_threads.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_threads.xml deleted file mode 100644 index a1de9e0560..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_threads.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_threads_solid.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_threads_solid.xml deleted file mode 100644 index 496ca795aa..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_threads_solid.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_user.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_user.xml deleted file mode 100644 index 5f61985ce4..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_user.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_user_add.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_user_add.xml deleted file mode 100644 index 5e8d7aa2c2..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_user_add.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_user_add_solid.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_user_add_solid.xml deleted file mode 100644 index 98fbd006de..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_user_add_solid.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_user_profile.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_user_profile.xml deleted file mode 100644 index 8027856173..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_user_profile.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_verified.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_verified.xml deleted file mode 100644 index 9066bf0240..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_verified.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_video_call.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_video_call.xml deleted file mode 100644 index 6000e9e8dc..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_video_call.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_video_call_declined.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_video_call_declined.xml deleted file mode 100644 index 1a9cfb0542..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_video_call_declined.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_video_call_missed.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_video_call_missed.xml deleted file mode 100644 index eb5f1b5607..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_video_call_missed.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_video_call_off.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_video_call_off.xml deleted file mode 100644 index 69f256c365..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_video_call_off.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_visibility_off.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_visibility_off.xml deleted file mode 100644 index 739f147e09..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_visibility_off.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_visibility_on.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_visibility_on.xml deleted file mode 100644 index ec24b596af..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_visibility_on.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_voice_call.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_voice_call.xml deleted file mode 100644 index 091eca5df4..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_voice_call.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_web_browser.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_web_browser.xml deleted file mode 100644 index ae31c62172..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_web_browser.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_edit_solid.xml b/libraries/designsystem/src/main/res/drawable/ic_edit_solid.xml deleted file mode 100644 index 7c7b628201..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_edit_solid.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_pause.xml b/libraries/designsystem/src/main/res/drawable/ic_pause.xml index bc6deee55a..8c23a73376 100644 --- a/libraries/designsystem/src/main/res/drawable/ic_pause.xml +++ b/libraries/designsystem/src/main/res/drawable/ic_pause.xml @@ -5,5 +5,5 @@ android:viewportHeight="20"> + android:fillColor="@android:color/white"/> diff --git a/libraries/designsystem/src/main/res/drawable/ic_poll_end.xml b/libraries/designsystem/src/main/res/drawable/ic_poll_end.xml deleted file mode 100644 index 1299f4078f..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_poll_end.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - diff --git a/features/messages/impl/src/main/res/drawable/retry.xml b/libraries/designsystem/src/main/res/drawable/ic_retry.xml similarity index 96% rename from features/messages/impl/src/main/res/drawable/retry.xml rename to libraries/designsystem/src/main/res/drawable/ic_retry.xml index c3fda8bca6..1990c52f0c 100644 --- a/features/messages/impl/src/main/res/drawable/retry.xml +++ b/libraries/designsystem/src/main/res/drawable/ic_retry.xml @@ -5,5 +5,5 @@ android:viewportHeight="24"> + android:fillColor="@android:color/white"/> diff --git a/libraries/designsystem/src/main/res/drawable/ic_send.xml b/libraries/designsystem/src/main/res/drawable/ic_send.xml deleted file mode 100644 index 8b90be336e..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_send.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_thread_decoration.xml b/libraries/designsystem/src/main/res/drawable/ic_thread_decoration.xml deleted file mode 100644 index b90c22f6b5..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_thread_decoration.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_video_call.xml b/libraries/designsystem/src/main/res/drawable/ic_video_call.xml deleted file mode 100644 index cd184f9448..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_video_call.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/colors/AvatarColorsTest.kt b/libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/colors/AvatarColorsTest.kt index 7b3392607d..2db042cdd0 100644 --- a/libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/colors/AvatarColorsTest.kt +++ b/libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/colors/AvatarColorsTest.kt @@ -17,8 +17,8 @@ package io.element.android.libraries.designsystem.colors import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.theme.colors.avatarColorsDark -import io.element.android.libraries.theme.colors.avatarColorsLight +import io.element.android.compound.theme.avatarColorsDark +import io.element.android.compound.theme.avatarColorsLight import org.junit.Test class AvatarColorsTest { diff --git a/libraries/eventformatter/impl/build.gradle.kts b/libraries/eventformatter/impl/build.gradle.kts index 3bee2df488..1fa92f1e25 100644 --- a/libraries/eventformatter/impl/build.gradle.kts +++ b/libraries/eventformatter/impl/build.gradle.kts @@ -40,6 +40,7 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) + implementation(projects.libraries.matrixui) implementation(projects.libraries.uiStrings) implementation(projects.services.toolbox.api) api(projects.libraries.eventformatter.api) diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt index aad89c5c2c..9475275e5d 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt @@ -25,7 +25,6 @@ import com.squareup.anvil.annotations.ContributesBinding import io.element.android.libraries.di.SessionScope import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter import io.element.android.libraries.eventformatter.impl.mode.RenderingMode -import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem @@ -48,9 +47,9 @@ import io.element.android.libraries.matrix.api.timeline.item.event.StickerConten import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent -import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType +import io.element.android.libraries.matrix.ui.messages.toPlainText import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.toolbox.api.strings.StringProvider import javax.inject.Inject @@ -58,14 +57,13 @@ import javax.inject.Inject @ContributesBinding(SessionScope::class) class DefaultRoomLastMessageFormatter @Inject constructor( private val sp: StringProvider, - private val matrixClient: MatrixClient, private val roomMembershipContentFormatter: RoomMembershipContentFormatter, private val profileChangeContentFormatter: ProfileChangeContentFormatter, private val stateContentFormatter: StateContentFormatter, ) : RoomLastMessageFormatter { override fun format(event: EventTimelineItem, isDmRoom: Boolean): CharSequence? { - val isOutgoing = matrixClient.isMe(event.sender) + val isOutgoing = event.isOwn val senderDisplayName = (event.senderProfile as? ProfileTimelineDetails.Ready)?.displayName ?: event.sender.value return when (val content = event.content) { is MessageContent -> processMessageContents(content, senderDisplayName, isDmRoom) @@ -114,7 +112,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor( return "* $senderDisplayName ${messageType.body}" } is TextMessageType -> { - messageType.body + messageType.toPlainText() } is VideoMessageType -> { sp.getString(CommonStrings.common_video) @@ -137,11 +135,6 @@ class DefaultRoomLastMessageFormatter @Inject constructor( is OtherMessageType -> { messageType.body } - UnknownMessageType -> { - // Display the body as a fallback, but should not happen anymore - // (we have `OtherMessageType` now) - messageContent.body - } is NoticeMessageType -> { messageType.body } diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt index 2b729fb0d5..ce4e4aa860 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt @@ -21,7 +21,6 @@ import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.di.SessionScope import io.element.android.libraries.eventformatter.api.TimelineEventFormatter import io.element.android.libraries.eventformatter.impl.mode.RenderingMode -import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent @@ -42,7 +41,6 @@ import javax.inject.Inject @ContributesBinding(SessionScope::class) class DefaultTimelineEventFormatter @Inject constructor( private val sp: StringProvider, - private val matrixClient: MatrixClient, private val buildMeta: BuildMeta, private val roomMembershipContentFormatter: RoomMembershipContentFormatter, private val profileChangeContentFormatter: ProfileChangeContentFormatter, @@ -50,7 +48,7 @@ class DefaultTimelineEventFormatter @Inject constructor( ) : TimelineEventFormatter { override fun format(event: EventTimelineItem): CharSequence? { - val isOutgoing = matrixClient.isMe(event.sender) + val isOutgoing = event.isOwn val senderDisplayName = (event.senderProfile as? ProfileTimelineDetails.Ready)?.displayName ?: event.sender.value return when (val content = event.content) { is RoomMembershipContent -> { diff --git a/libraries/eventformatter/impl/src/main/res/values-hu/translations.xml b/libraries/eventformatter/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..dd38dcbbe4 --- /dev/null +++ b/libraries/eventformatter/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,57 @@ + + + "(a profilkép is megváltozott)" + "%1$s megváltoztatta a profilképét" + "Megváltoztatta a profilképét" + "%1$s megváltoztatta a megjelenítendő nevét: %2$s → %3$s" + "Megváltoztatta a megjelenítendő nevét: %1$s → %2$s" + "%1$s eltávolította a megjelenítendő nevét (ez volt: %2$s)" + "Eltávolította a megjelenítendő nevét (ez volt: %1$s)" + "%1$s beállította a megjelenítendő nevét: %2$s" + "Beállította a megjelenítendő nevét: %1$s" + "%1$s megváltoztatta a szoba profilképét" + "Megváltoztatta a szoba profilképét" + "%1$s eltávolította a szoba profilképét" + "Eltávolítottad a szoba profilképét" + "%1$s kitiltotta: %2$s" + "Kitiltotta: %1$s" + "%1$s létrehozta a szobát" + "Létrehozta a szobát" + "%1$s meghívta: %2$s" + "%1$s elfogadta a meghívást" + "Elfogadta a meghívást" + "Meghívta: %1$s" + "%1$s meghívta" + "%1$s csatlakozott a szobához" + "Csatlakozott a szobához" + "%1$s kérte, hogy csatlakozhasson" + "%1$s engedélyezte, hogy %2$s csatlakozhasson" + "Engedélyezte, hogy %1$s csatlakozhasson" + "Kérte, hogy csatlakozhasson" + "%1$s elutasította %2$s kérését, hogy csatlakozhasson" + "Elutasította %1$s kérését, hogy csatlakozhasson" + "%1$s elutasította a kérését, hogy csatlakozhasson" + "%1$s már nem akar csatlakozni" + "Lemondta a csatlakozási kérését" + "%1$s elhagyta a szobát" + "Elhagyta a szobát" + "%1$s megváltoztatta a szoba nevét: %2$s" + "Megváltoztatta a szoba nevét: %1$s" + "%1$s eltávolította a szoba nevét" + "Eltávolította a szoba nevét" + "%1$s elutasította a meghívást" + "Elutasította a meghívást" + "%1$s eltávolította: %2$s" + "Eltávolította: %1$s" + "%1$s meghívót küldött %2$s számára, hogy csatlakozzon a szobához" + "Meghívót küldött %1$s számára, hogy csatlakozzon a szobához" + "%1$s visszavonta %2$s meghívását, hogy csatlakozzon a szobához" + "Visszavonta %1$s meghívását, hogy csatlakozzon a szobához" + "%1$s megváltoztatta a témát: %2$s" + "Megváltoztatta a témát: %1$s" + "%1$s eltávolította a szoba témáját" + "Eltávolította a szoba témáját" + "%1$s visszavonta %2$s kitiltását" + "Visszavonta %1$s kitiltását" + "%1$s ismeretlen változást hajtott végre a tagságában" + diff --git a/libraries/eventformatter/impl/src/main/res/values-in/translations.xml b/libraries/eventformatter/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..67ba9b03be --- /dev/null +++ b/libraries/eventformatter/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,57 @@ + + + "(avatar juga diubah)" + "%1$s mengubah avatarnya" + "Anda mengubah avatar sendiri" + "%1$s mengubah nama tampilannya dari %2$s menjadi %3$s" + "Anda mengubah nama tampilan sendiri dari %1$s menjadi %2$s" + "%1$s menghapus nama tampilannya (sebelumnya %2$s)" + "Anda menghapus nama tampilan sendiri (sebelumnya %1$s)" + "%1$s menetapkan nama tampilannya menjadi %2$s" + "Anda menetapkan nama tampilan sendiri menjadi %1$s" + "%1$s mengubah avatar ruangan" + "Anda mengubah avatar ruangan" + "%1$s menghapus avatar ruangan" + "Anda menghapus avatar ruangan" + "%1$s memblokir %2$s" + "Anda memblokir %1$s" + "%1$s membuat ruangan" + "Anda membuat ruangan" + "%1$s mengundang %2$s" + "%1$s menerima undangan" + "Anda menerima undangan" + "Anda mengundang %1$s" + "%1$s mengundang Anda" + "%1$s bergabung ke ruangan" + "Anda bergabung ke ruangan" + "%1$s meminta untuk bergabung" + "%1$s memperbolehkan %2$s untuk bergabung" + "%1$s memperbolehkan Anda untuk bergabung" + "Anda meminta untuk bergabung" + "%1$s menolak permintaan %2$s untuk bergabung" + "Anda menolak permintaan %1$s untuk bergabung" + "%1$s menolak permintaan Anda untuk bergabung" + "%1$s tidak lagi tertarik untuk bergabung" + "Anda membatalkan permintaan sendiri untuk bergabung" + "%1$s meninggalkan ruangan" + "Anda keluar dari ruangan" + "%1$s mengubah nama ruangan menjadi: %2$s" + "Anda mengubah nama ruangan menjadi: %1$s" + "%1$s menghapus nama ruangan" + "Anda menghapus nama ruangan" + "%1$s menolak undangan" + "Anda menolak undangan" + "%1$s mengeluarkan %2$s" + "Anda mengeluarkan %1$s" + "%1$s mengirimkan undangan kepada %2$s untuk bergabung ke ruangan" + "Anda mengirimkan undangan kepada %1$s untuk bergabung ke ruangan" + "%1$s menghapus undangan kepada %2$s untuk bergabung ke ruangan" + "Anda menghapus undangan kepada %1$s untuk bergabung ke ruangan" + "%1$s mengubah topik menjadi: %2$s" + "Anda mengubah topik menjadi: %1$s" + "%1$s menghapus topik ruangan" + "Anda menghapus topik ruangan" + "%1$s membatalkan pemblokiran %2$s" + "Anda membatalkan pemblokiran %1$s" + "%1$s membuat perubahan keanggotaan yang tidak diketahui" + diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt index f7b98626e6..a6d0f2e776 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt @@ -18,7 +18,8 @@ package io.element.android.libraries.eventformatter.impl import android.content.Context import androidx.compose.ui.text.AnnotatedString -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.media.ImageInfo import io.element.android.libraries.matrix.api.media.MediaSource @@ -45,14 +46,13 @@ import io.element.android.libraries.matrix.api.timeline.item.event.StickerConten import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent -import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.FakeMatrixClient -import io.element.android.libraries.matrix.test.room.aPollContent -import io.element.android.libraries.matrix.test.room.aProfileChangeMessageContent -import io.element.android.libraries.matrix.test.room.anEventTimelineItem +import io.element.android.libraries.matrix.test.timeline.aPollContent +import io.element.android.libraries.matrix.test.timeline.aProfileChangeMessageContent +import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem import io.element.android.services.toolbox.impl.strings.AndroidStringProvider import org.junit.Before import org.junit.Test @@ -76,7 +76,6 @@ class DefaultRoomLastMessageFormatterTest { val stringProvider = AndroidStringProvider(context.resources) formatter = DefaultRoomLastMessageFormatter( sp = AndroidStringProvider(context.resources), - matrixClient = fakeMatrixClient, roomMembershipContentFormatter = RoomMembershipContentFormatter(fakeMatrixClient, stringProvider), profileChangeContentFormatter = ProfileChangeContentFormatter(stringProvider), stateContentFormatter = StateContentFormatter(stringProvider) @@ -92,10 +91,10 @@ class DefaultRoomLastMessageFormatterTest { val message = createRoomEvent(false, senderName, RedactedContent) val result = formatter.format(message, isDm) if (isDm) { - Truth.assertThat(result).isEqualTo(expected) + assertThat(result).isEqualTo(expected) } else { - Truth.assertThat(result).isInstanceOf(AnnotatedString::class.java) - Truth.assertThat(result.toString()).isEqualTo("$senderName: $expected") + assertThat(result).isInstanceOf(AnnotatedString::class.java) + assertThat(result.toString()).isEqualTo("$senderName: $expected") } } } @@ -107,7 +106,7 @@ class DefaultRoomLastMessageFormatterTest { val info = ImageInfo(null, null, null, null, null, null, null) val message = createRoomEvent(false, null, StickerContent(body, info, "url")) val result = formatter.format(message, false) - Truth.assertThat(result).isEqualTo(body) + assertThat(result).isEqualTo(body) } @Test @@ -119,10 +118,10 @@ class DefaultRoomLastMessageFormatterTest { val message = createRoomEvent(false, senderName, UnableToDecryptContent(UnableToDecryptContent.Data.Unknown)) val result = formatter.format(message, isDm) if (isDm) { - Truth.assertThat(result).isEqualTo(expected) + assertThat(result).isEqualTo(expected) } else { - Truth.assertThat(result).isInstanceOf(AnnotatedString::class.java) - Truth.assertThat(result.toString()).isEqualTo("$senderName: $expected") + assertThat(result).isInstanceOf(AnnotatedString::class.java) + assertThat(result.toString()).isEqualTo("$senderName: $expected") } } } @@ -141,10 +140,10 @@ class DefaultRoomLastMessageFormatterTest { val message = createRoomEvent(false, senderName, type) val result = formatter.format(message, isDm) if (isDm) { - Truth.assertWithMessage("$type was not properly handled").that(result).isEqualTo(expected) + assertWithMessage("$type was not properly handled").that(result).isEqualTo(expected) } else { - Truth.assertWithMessage("$type does not create an AnnotatedString").that(result).isInstanceOf(AnnotatedString::class.java) - Truth.assertWithMessage("$type was not properly handled").that(result.toString()).isEqualTo("$senderName: $expected") + assertWithMessage("$type does not create an AnnotatedString").that(result).isInstanceOf(AnnotatedString::class.java) + assertWithMessage("$type was not properly handled").that(result.toString()).isEqualTo("$senderName: $expected") } } } @@ -170,6 +169,7 @@ class DefaultRoomLastMessageFormatterTest { LocationMessageType(body, "geo:1,2", null), NoticeMessageType(body, null), EmoteMessageType(body, null), + OtherMessageType(msgType = "a_type", body = body), ) val senderName = "Someone" val resultsInRoom = mutableListOf>() @@ -187,13 +187,6 @@ class DefaultRoomLastMessageFormatterTest { resultsInRoom.add(type to result) } } - val unknownMessage = createRoomEvent(sentByYou = false, senderDisplayName = "Someone", content = createMessageContent(UnknownMessageType)) - val result = UnknownMessageType to formatter.format(unknownMessage, isDmRoom = isDm) - if (isDm) { - resultsInDm.add(result) - } else { - resultsInRoom.add(result) - } } // Verify results of DM mode @@ -208,10 +201,9 @@ class DefaultRoomLastMessageFormatterTest { is EmoteMessageType -> "* $senderName ${type.body}" is TextMessageType, is NoticeMessageType, - is OtherMessageType, - UnknownMessageType -> body + is OtherMessageType -> body } - Truth.assertWithMessage("$type was not properly handled for DM").that(result).isEqualTo(expectedResult) + assertWithMessage("$type was not properly handled for DM").that(result).isEqualTo(expectedResult) } // Verify results of Room mode @@ -226,8 +218,7 @@ class DefaultRoomLastMessageFormatterTest { is LocationMessageType -> "$senderName: Shared location" is TextMessageType, is NoticeMessageType, - is OtherMessageType, - UnknownMessageType -> "$senderName: $body" + is OtherMessageType -> "$senderName: $body" is EmoteMessageType -> "* $senderName ${type.body}" } val shouldCreateAnnotatedString = when (type) { @@ -240,14 +231,13 @@ class DefaultRoomLastMessageFormatterTest { is EmoteMessageType -> false is TextMessageType, is NoticeMessageType -> true is OtherMessageType -> true - UnknownMessageType -> true } if (shouldCreateAnnotatedString) { - Truth.assertWithMessage("$type doesn't produce an AnnotatedString") + assertWithMessage("$type doesn't produce an AnnotatedString") .that(result) .isInstanceOf(AnnotatedString::class.java) } - Truth.assertWithMessage("$type was not properly handled for room").that(string).isEqualTo(expectedResult) + assertWithMessage("$type was not properly handled for room").that(string).isEqualTo(expectedResult) } } @@ -264,11 +254,11 @@ class DefaultRoomLastMessageFormatterTest { val youJoinedRoomEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent) val youJoinedRoom = formatter.format(youJoinedRoomEvent, false) - Truth.assertThat(youJoinedRoom).isEqualTo("You joined the room") + assertThat(youJoinedRoom).isEqualTo("You joined the room") val someoneJoinedRoomEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent) val someoneJoinedRoom = formatter.format(someoneJoinedRoomEvent, false) - Truth.assertThat(someoneJoinedRoom).isEqualTo("${someoneContent.userId} joined the room") + assertThat(someoneJoinedRoom).isEqualTo("${someoneContent.userId} joined the room") } @Test @@ -280,11 +270,11 @@ class DefaultRoomLastMessageFormatterTest { val youLeftRoomEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent) val youLeftRoom = formatter.format(youLeftRoomEvent, false) - Truth.assertThat(youLeftRoom).isEqualTo("You left the room") + assertThat(youLeftRoom).isEqualTo("You left the room") val someoneLeftRoomEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent) val someoneLeftRoom = formatter.format(someoneLeftRoomEvent, false) - Truth.assertThat(someoneLeftRoom).isEqualTo("${someoneContent.userId} left the room") + assertThat(someoneLeftRoom).isEqualTo("${someoneContent.userId} left the room") } @Test @@ -298,19 +288,19 @@ class DefaultRoomLastMessageFormatterTest { val youBannedEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent) val youBanned = formatter.format(youBannedEvent, false) - Truth.assertThat(youBanned).isEqualTo("You banned ${youContent.userId}") + assertThat(youBanned).isEqualTo("You banned ${youContent.userId}") val youKickBannedEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youKickedContent) val youKickedBanned = formatter.format(youKickBannedEvent, false) - Truth.assertThat(youKickedBanned).isEqualTo("You banned ${youContent.userId}") + assertThat(youKickedBanned).isEqualTo("You banned ${youContent.userId}") val someoneBannedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent) val someoneBanned = formatter.format(someoneBannedEvent, false) - Truth.assertThat(someoneBanned).isEqualTo("$otherName banned ${someoneContent.userId}") + assertThat(someoneBanned).isEqualTo("$otherName banned ${someoneContent.userId}") val someoneKickBannedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneKickedContent) val someoneKickBanned = formatter.format(someoneKickBannedEvent, false) - Truth.assertThat(someoneKickBanned).isEqualTo("$otherName banned ${someoneContent.userId}") + assertThat(someoneKickBanned).isEqualTo("$otherName banned ${someoneContent.userId}") } @Test @@ -322,11 +312,11 @@ class DefaultRoomLastMessageFormatterTest { val youUnbannedEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent) val youUnbanned = formatter.format(youUnbannedEvent, false) - Truth.assertThat(youUnbanned).isEqualTo("You unbanned ${youContent.userId}") + assertThat(youUnbanned).isEqualTo("You unbanned ${youContent.userId}") val someoneUnbannedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent) val someoneUnbanned = formatter.format(someoneUnbannedEvent, false) - Truth.assertThat(someoneUnbanned).isEqualTo("$otherName unbanned ${someoneContent.userId}") + assertThat(someoneUnbanned).isEqualTo("$otherName unbanned ${someoneContent.userId}") } @Test @@ -338,11 +328,11 @@ class DefaultRoomLastMessageFormatterTest { val youKickedEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent) val youKicked = formatter.format(youKickedEvent, false) - Truth.assertThat(youKicked).isEqualTo("You removed ${youContent.userId}") + assertThat(youKicked).isEqualTo("You removed ${youContent.userId}") val someoneKickedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent) val someoneKicked = formatter.format(someoneKickedEvent, false) - Truth.assertThat(someoneKicked).isEqualTo("$otherName removed ${someoneContent.userId}") + assertThat(someoneKicked).isEqualTo("$otherName removed ${someoneContent.userId}") } @Test @@ -354,15 +344,15 @@ class DefaultRoomLastMessageFormatterTest { val youWereInvitedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = youContent) val youWereInvited = formatter.format(youWereInvitedEvent, false) - Truth.assertThat(youWereInvited).isEqualTo("$otherName invited you") + assertThat(youWereInvited).isEqualTo("$otherName invited you") val youInvitedEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = someoneContent) val youInvited = formatter.format(youInvitedEvent, false) - Truth.assertThat(youInvited).isEqualTo("You invited ${someoneContent.userId}") + assertThat(youInvited).isEqualTo("You invited ${someoneContent.userId}") val someoneInvitedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent) val someoneInvited = formatter.format(someoneInvitedEvent, false) - Truth.assertThat(someoneInvited).isEqualTo("$otherName invited ${someoneContent.userId}") + assertThat(someoneInvited).isEqualTo("$otherName invited ${someoneContent.userId}") } @Test @@ -374,11 +364,11 @@ class DefaultRoomLastMessageFormatterTest { val youAcceptedInviteEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent) val youAcceptedInvite = formatter.format(youAcceptedInviteEvent, false) - Truth.assertThat(youAcceptedInvite).isEqualTo("You accepted the invite") + assertThat(youAcceptedInvite).isEqualTo("You accepted the invite") val someoneAcceptedInviteEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent) val someoneAcceptedInvite = formatter.format(someoneAcceptedInviteEvent, false) - Truth.assertThat(someoneAcceptedInvite).isEqualTo("${someoneContent.userId} accepted the invite") + assertThat(someoneAcceptedInvite).isEqualTo("${someoneContent.userId} accepted the invite") } @Test @@ -390,11 +380,11 @@ class DefaultRoomLastMessageFormatterTest { val youRejectedInviteEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent) val youRejectedInvite = formatter.format(youRejectedInviteEvent, false) - Truth.assertThat(youRejectedInvite).isEqualTo("You rejected the invitation") + assertThat(youRejectedInvite).isEqualTo("You rejected the invitation") val someoneRejectedInviteEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent) val someoneRejectedInvite = formatter.format(someoneRejectedInviteEvent, false) - Truth.assertThat(someoneRejectedInvite).isEqualTo("${someoneContent.userId} rejected the invitation") + assertThat(someoneRejectedInvite).isEqualTo("${someoneContent.userId} rejected the invitation") } @Test @@ -405,11 +395,11 @@ class DefaultRoomLastMessageFormatterTest { val youRevokedInviteEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = someoneContent) val youRevokedInvite = formatter.format(youRevokedInviteEvent, false) - Truth.assertThat(youRevokedInvite).isEqualTo("You revoked the invitation for ${someoneContent.userId} to join the room") + assertThat(youRevokedInvite).isEqualTo("You revoked the invitation for ${someoneContent.userId} to join the room") val someoneRevokedInviteEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent) val someoneRevokedInvite = formatter.format(someoneRevokedInviteEvent, false) - Truth.assertThat(someoneRevokedInvite).isEqualTo("$otherName revoked the invitation for ${someoneContent.userId} to join the room") + assertThat(someoneRevokedInvite).isEqualTo("$otherName revoked the invitation for ${someoneContent.userId} to join the room") } @Test @@ -421,11 +411,11 @@ class DefaultRoomLastMessageFormatterTest { val youKnockedEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent) val youKnocked = formatter.format(youKnockedEvent, false) - Truth.assertThat(youKnocked).isEqualTo("You requested to join") + assertThat(youKnocked).isEqualTo("You requested to join") val someoneKnockedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent) val someoneKnocked = formatter.format(someoneKnockedEvent, false) - Truth.assertThat(someoneKnocked).isEqualTo("${someoneContent.userId} requested to join") + assertThat(someoneKnocked).isEqualTo("${someoneContent.userId} requested to join") } @Test @@ -436,11 +426,11 @@ class DefaultRoomLastMessageFormatterTest { val youAcceptedKnockEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = someoneContent) val youAcceptedKnock = formatter.format(youAcceptedKnockEvent, false) - Truth.assertThat(youAcceptedKnock).isEqualTo("${someoneContent.userId} allowed you to join") + assertThat(youAcceptedKnock).isEqualTo("${someoneContent.userId} allowed you to join") val someoneAcceptedKnockEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent) val someoneAcceptedKnock = formatter.format(someoneAcceptedKnockEvent, false) - Truth.assertThat(someoneAcceptedKnock).isEqualTo("$otherName allowed ${someoneContent.userId} to join") + assertThat(someoneAcceptedKnock).isEqualTo("$otherName allowed ${someoneContent.userId} to join") } @Test @@ -452,11 +442,11 @@ class DefaultRoomLastMessageFormatterTest { val youRetractedKnockEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent) val youRetractedKnock = formatter.format(youRetractedKnockEvent, false) - Truth.assertThat(youRetractedKnock).isEqualTo("You cancelled your request to join") + assertThat(youRetractedKnock).isEqualTo("You cancelled your request to join") val someoneRetractedKnockEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent) val someoneRetractedKnock = formatter.format(someoneRetractedKnockEvent, false) - Truth.assertThat(someoneRetractedKnock).isEqualTo("${someoneContent.userId} is no longer interested in joining") + assertThat(someoneRetractedKnock).isEqualTo("${someoneContent.userId} is no longer interested in joining") } @Test @@ -468,15 +458,15 @@ class DefaultRoomLastMessageFormatterTest { val youDeniedKnockEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = someoneContent) val youDeniedKnock = formatter.format(youDeniedKnockEvent, false) - Truth.assertThat(youDeniedKnock).isEqualTo("You rejected ${someoneContent.userId}'s request to join") + assertThat(youDeniedKnock).isEqualTo("You rejected ${someoneContent.userId}'s request to join") val someoneDeniedKnockEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent) val someoneDeniedKnock = formatter.format(someoneDeniedKnockEvent, false) - Truth.assertThat(someoneDeniedKnock).isEqualTo("$otherName rejected ${someoneContent.userId}'s request to join") + assertThat(someoneDeniedKnock).isEqualTo("$otherName rejected ${someoneContent.userId}'s request to join") val someoneDeniedYourKnockEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = youContent) val someoneDeniedYourKnock = formatter.format(someoneDeniedYourKnockEvent, false) - Truth.assertThat(someoneDeniedYourKnock).isEqualTo("$otherName rejected your request to join") + assertThat(someoneDeniedYourKnock).isEqualTo("$otherName rejected your request to join") } @Test @@ -491,7 +481,7 @@ class DefaultRoomLastMessageFormatterTest { change to result } val expected = otherChanges.map { it to null } - Truth.assertThat(results).isEqualTo(expected) + assertThat(results).isEqualTo(expected) } // endregion @@ -507,19 +497,19 @@ class DefaultRoomLastMessageFormatterTest { val youChangedRoomAvatarEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent) val youChangedRoomAvatar = formatter.format(youChangedRoomAvatarEvent, false) - Truth.assertThat(youChangedRoomAvatar).isEqualTo("You changed the room avatar") + assertThat(youChangedRoomAvatar).isEqualTo("You changed the room avatar") val someoneChangedRoomAvatarEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent) val someoneChangedRoomAvatar = formatter.format(someoneChangedRoomAvatarEvent, false) - Truth.assertThat(someoneChangedRoomAvatar).isEqualTo("$otherName changed the room avatar") + assertThat(someoneChangedRoomAvatar).isEqualTo("$otherName changed the room avatar") val youRemovedRoomAvatarEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent) val youRemovedRoomAvatar = formatter.format(youRemovedRoomAvatarEvent, false) - Truth.assertThat(youRemovedRoomAvatar).isEqualTo("You removed the room avatar") + assertThat(youRemovedRoomAvatar).isEqualTo("You removed the room avatar") val someoneRemovedRoomAvatarEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent) val someoneRemovedRoomAvatar = formatter.format(someoneRemovedRoomAvatarEvent, false) - Truth.assertThat(someoneRemovedRoomAvatar).isEqualTo("$otherName removed the room avatar") + assertThat(someoneRemovedRoomAvatar).isEqualTo("$otherName removed the room avatar") } @Test @@ -530,11 +520,11 @@ class DefaultRoomLastMessageFormatterTest { val youCreatedRoomMessage = createRoomEvent(sentByYou = true, senderDisplayName = null, content = content) val youCreatedRoom = formatter.format(youCreatedRoomMessage, false) - Truth.assertThat(youCreatedRoom).isEqualTo("You created the room") + assertThat(youCreatedRoom).isEqualTo("You created the room") val someoneCreatedRoomEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = content) val someoneCreatedRoom = formatter.format(someoneCreatedRoomEvent, false) - Truth.assertThat(someoneCreatedRoom).isEqualTo("$otherName created the room") + assertThat(someoneCreatedRoom).isEqualTo("$otherName created the room") } @Test @@ -545,11 +535,11 @@ class DefaultRoomLastMessageFormatterTest { val youCreatedRoomMessage = createRoomEvent(sentByYou = true, senderDisplayName = null, content = content) val youCreatedRoom = formatter.format(youCreatedRoomMessage, false) - Truth.assertThat(youCreatedRoom).isEqualTo("Encryption enabled") + assertThat(youCreatedRoom).isEqualTo("Encryption enabled") val someoneCreatedRoomEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = content) val someoneCreatedRoom = formatter.format(someoneCreatedRoomEvent, false) - Truth.assertThat(someoneCreatedRoom).isEqualTo("Encryption enabled") + assertThat(someoneCreatedRoom).isEqualTo("Encryption enabled") } @Test @@ -562,19 +552,19 @@ class DefaultRoomLastMessageFormatterTest { val youChangedRoomNameEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent) val youChangedRoomName = formatter.format(youChangedRoomNameEvent, false) - Truth.assertThat(youChangedRoomName).isEqualTo("You changed the room name to: $newName") + assertThat(youChangedRoomName).isEqualTo("You changed the room name to: $newName") val someoneChangedRoomNameEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent) val someoneChangedRoomName = formatter.format(someoneChangedRoomNameEvent, false) - Truth.assertThat(someoneChangedRoomName).isEqualTo("$otherName changed the room name to: $newName") + assertThat(someoneChangedRoomName).isEqualTo("$otherName changed the room name to: $newName") val youRemovedRoomNameEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent) val youRemovedRoomName = formatter.format(youRemovedRoomNameEvent, false) - Truth.assertThat(youRemovedRoomName).isEqualTo("You removed the room name") + assertThat(youRemovedRoomName).isEqualTo("You removed the room name") val someoneRemovedRoomNameEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent) val someoneRemovedRoomName = formatter.format(someoneRemovedRoomNameEvent, false) - Truth.assertThat(someoneRemovedRoomName).isEqualTo("$otherName removed the room name") + assertThat(someoneRemovedRoomName).isEqualTo("$otherName removed the room name") } @Test @@ -587,19 +577,19 @@ class DefaultRoomLastMessageFormatterTest { val youInvitedSomeoneEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent) val youInvitedSomeone = formatter.format(youInvitedSomeoneEvent, false) - Truth.assertThat(youInvitedSomeone).isEqualTo("You sent an invitation to $inviteeName to join the room") + assertThat(youInvitedSomeone).isEqualTo("You sent an invitation to $inviteeName to join the room") val someoneInvitedSomeoneEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent) val someoneInvitedSomeone = formatter.format(someoneInvitedSomeoneEvent, false) - Truth.assertThat(someoneInvitedSomeone).isEqualTo("$otherName sent an invitation to $inviteeName to join the room") + assertThat(someoneInvitedSomeone).isEqualTo("$otherName sent an invitation to $inviteeName to join the room") val youInvitedNoOneEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent) val youInvitedNoOne = formatter.format(youInvitedNoOneEvent, false) - Truth.assertThat(youInvitedNoOne).isNull() + assertThat(youInvitedNoOne).isNull() val someoneInvitedNoOneEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent) val someoneInvitedNoOne = formatter.format(someoneInvitedNoOneEvent, false) - Truth.assertThat(someoneInvitedNoOne).isNull() + assertThat(someoneInvitedNoOne).isNull() } @Test @@ -612,19 +602,19 @@ class DefaultRoomLastMessageFormatterTest { val youChangedRoomTopicEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent) val youChangedRoomTopic = formatter.format(youChangedRoomTopicEvent, false) - Truth.assertThat(youChangedRoomTopic).isEqualTo("You changed the topic to: $roomTopic") + assertThat(youChangedRoomTopic).isEqualTo("You changed the topic to: $roomTopic") val someoneChangedRoomTopicEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent) val someoneChangedRoomTopic = formatter.format(someoneChangedRoomTopicEvent, false) - Truth.assertThat(someoneChangedRoomTopic).isEqualTo("$otherName changed the topic to: $roomTopic") + assertThat(someoneChangedRoomTopic).isEqualTo("$otherName changed the topic to: $roomTopic") val youRemovedRoomTopicEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent) val youRemovedRoomTopic = formatter.format(youRemovedRoomTopicEvent, false) - Truth.assertThat(youRemovedRoomTopic).isEqualTo("You removed the room topic") + assertThat(youRemovedRoomTopic).isEqualTo("You removed the room topic") val someoneRemovedRoomTopicEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent) val someoneRemovedRoomTopic = formatter.format(someoneRemovedRoomTopicEvent, false) - Truth.assertThat(someoneRemovedRoomTopic).isEqualTo("$otherName removed the room topic") + assertThat(someoneRemovedRoomTopic).isEqualTo("$otherName removed the room topic") } @Test @@ -643,7 +633,7 @@ class DefaultRoomLastMessageFormatterTest { state to result } val expected = otherStates.map { it to null } - Truth.assertThat(results).isEqualTo(expected) + assertThat(results).isEqualTo(expected) } // endregion @@ -662,35 +652,35 @@ class DefaultRoomLastMessageFormatterTest { val youChangedAvatarEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent) val youChangedAvatar = formatter.format(youChangedAvatarEvent, false) - Truth.assertThat(youChangedAvatar).isEqualTo("You changed your avatar") + assertThat(youChangedAvatar).isEqualTo("You changed your avatar") val someoneChangeAvatarEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent) val someoneChangeAvatar = formatter.format(someoneChangeAvatarEvent, false) - Truth.assertThat(someoneChangeAvatar).isEqualTo("$otherName changed their avatar") + assertThat(someoneChangeAvatar).isEqualTo("$otherName changed their avatar") val youSetAvatarEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = setContent) val youSetAvatar = formatter.format(youSetAvatarEvent, false) - Truth.assertThat(youSetAvatar).isEqualTo("You changed your avatar") + assertThat(youSetAvatar).isEqualTo("You changed your avatar") val someoneSetAvatarEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = setContent) val someoneSetAvatar = formatter.format(someoneSetAvatarEvent, false) - Truth.assertThat(someoneSetAvatar).isEqualTo("$otherName changed their avatar") + assertThat(someoneSetAvatar).isEqualTo("$otherName changed their avatar") val youRemovedAvatarEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent) val youRemovedAvatar = formatter.format(youRemovedAvatarEvent, false) - Truth.assertThat(youRemovedAvatar).isEqualTo("You changed your avatar") + assertThat(youRemovedAvatar).isEqualTo("You changed your avatar") val someoneRemovedAvatarEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent) val someoneRemovedAvatar = formatter.format(someoneRemovedAvatarEvent, false) - Truth.assertThat(someoneRemovedAvatar).isEqualTo("$otherName changed their avatar") + assertThat(someoneRemovedAvatar).isEqualTo("$otherName changed their avatar") val unchangedEvent = createRoomEvent(sentByYou = true, senderDisplayName = otherName, content = sameContent) val unchangedResult = formatter.format(unchangedEvent, false) - Truth.assertThat(unchangedResult).isNull() + assertThat(unchangedResult).isNull() val invalidEvent = createRoomEvent(sentByYou = true, senderDisplayName = otherName, content = invalidContent) val invalidResult = formatter.format(invalidEvent, false) - Truth.assertThat(invalidResult).isNull() + assertThat(invalidResult).isNull() } @Test @@ -707,35 +697,35 @@ class DefaultRoomLastMessageFormatterTest { val youChangedDisplayNameEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent) val youChangedDisplayName = formatter.format(youChangedDisplayNameEvent, false) - Truth.assertThat(youChangedDisplayName).isEqualTo("You changed your display name from $oldDisplayName to $newDisplayName") + assertThat(youChangedDisplayName).isEqualTo("You changed your display name from $oldDisplayName to $newDisplayName") val someoneChangedDisplayNameEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent) val someoneChangedDisplayName = formatter.format(someoneChangedDisplayNameEvent, false) - Truth.assertThat(someoneChangedDisplayName).isEqualTo("$otherName changed their display name from $oldDisplayName to $newDisplayName") + assertThat(someoneChangedDisplayName).isEqualTo("$otherName changed their display name from $oldDisplayName to $newDisplayName") val youSetDisplayNameEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = setContent) val youSetDisplayName = formatter.format(youSetDisplayNameEvent, false) - Truth.assertThat(youSetDisplayName).isEqualTo("You set your display name to $newDisplayName") + assertThat(youSetDisplayName).isEqualTo("You set your display name to $newDisplayName") val someoneSetDisplayNameEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = setContent) val someoneSetDisplayName = formatter.format(someoneSetDisplayNameEvent, false) - Truth.assertThat(someoneSetDisplayName).isEqualTo("$otherName set their display name to $newDisplayName") + assertThat(someoneSetDisplayName).isEqualTo("$otherName set their display name to $newDisplayName") val youRemovedDisplayNameEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent) val youRemovedDisplayName = formatter.format(youRemovedDisplayNameEvent, false) - Truth.assertThat(youRemovedDisplayName).isEqualTo("You removed your display name (it was $oldDisplayName)") + assertThat(youRemovedDisplayName).isEqualTo("You removed your display name (it was $oldDisplayName)") val someoneRemovedDisplayNameEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent) val someoneRemovedDisplayName = formatter.format(someoneRemovedDisplayNameEvent, false) - Truth.assertThat(someoneRemovedDisplayName).isEqualTo("$otherName removed their display name (it was $oldDisplayName)") + assertThat(someoneRemovedDisplayName).isEqualTo("$otherName removed their display name (it was $oldDisplayName)") val unchangedEvent = createRoomEvent(sentByYou = true, senderDisplayName = otherName, content = sameContent) val unchangedResult = formatter.format(unchangedEvent, false) - Truth.assertThat(unchangedResult).isNull() + assertThat(unchangedResult).isNull() val invalidEvent = createRoomEvent(sentByYou = true, senderDisplayName = otherName, content = invalidContent) val invalidResult = formatter.format(invalidEvent, false) - Truth.assertThat(invalidResult).isNull() + assertThat(invalidResult).isNull() } @Test @@ -764,15 +754,15 @@ class DefaultRoomLastMessageFormatterTest { val youChangedBothEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent) val youChangedBoth = formatter.format(youChangedBothEvent, false) - Truth.assertThat(youChangedBoth).isEqualTo("You changed your display name from $oldDisplayName to $newDisplayName\n(avatar was changed too)") + assertThat(youChangedBoth).isEqualTo("You changed your display name from $oldDisplayName to $newDisplayName\n(avatar was changed too)") val invalidContentEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = invalidContent) val invalidMessage = formatter.format(invalidContentEvent, false) - Truth.assertThat(invalidMessage).isNull() + assertThat(invalidMessage).isNull() val sameContentEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = sameContent) val sameMessage = formatter.format(sameContentEvent, false) - Truth.assertThat(sameMessage).isNull() + assertThat(sameMessage).isNull() } // endregion @@ -785,10 +775,10 @@ class DefaultRoomLastMessageFormatterTest { val pollContent = aPollContent() val mineContentEvent = createRoomEvent(sentByYou = true, senderDisplayName = "Alice", content = pollContent) - Truth.assertThat(formatter.format(mineContentEvent, true)).isEqualTo("Poll: Do you like polls?") + assertThat(formatter.format(mineContentEvent, true)).isEqualTo("Poll: Do you like polls?") val contentEvent = createRoomEvent(sentByYou = false, senderDisplayName = "Bob", content = pollContent) - Truth.assertThat(formatter.format(contentEvent, true)).isEqualTo("Poll: Do you like polls?") + assertThat(formatter.format(contentEvent, true)).isEqualTo("Poll: Do you like polls?") } @Test @@ -797,10 +787,10 @@ class DefaultRoomLastMessageFormatterTest { val pollContent = aPollContent() val mineContentEvent = createRoomEvent(sentByYou = true, senderDisplayName = "Alice", content = pollContent) - Truth.assertThat(formatter.format(mineContentEvent, false).toString()).isEqualTo("Alice: Poll: Do you like polls?") + assertThat(formatter.format(mineContentEvent, false).toString()).isEqualTo("Alice: Poll: Do you like polls?") val contentEvent = createRoomEvent(sentByYou = false, senderDisplayName = "Bob", content = pollContent) - Truth.assertThat(formatter.format(contentEvent, false).toString()).isEqualTo("Bob: Poll: Do you like polls?") + assertThat(formatter.format(contentEvent, false).toString()).isEqualTo("Bob: Poll: Do you like polls?") } // endregion @@ -808,6 +798,11 @@ class DefaultRoomLastMessageFormatterTest { private fun createRoomEvent(sentByYou: Boolean, senderDisplayName: String?, content: EventContent): EventTimelineItem { val sender = if (sentByYou) A_USER_ID else UserId("@someone_else:domain") val profile = ProfileTimelineDetails.Ready(senderDisplayName, false, null) - return anEventTimelineItem(content = content, senderProfile = profile, sender = sender) + return anEventTimelineItem( + content = content, + senderProfile = profile, + sender = sender, + isOwn = sentByYou, + ) } } diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/Feature.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/Feature.kt index 8343bca8e4..ee98091953 100644 --- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/Feature.kt +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/Feature.kt @@ -36,4 +36,11 @@ interface Feature { * The default value of the feature (enabled or disabled). */ val defaultValue: Boolean + + /** + * Whether the feature is finished or not. + * If false: the feature is still in development, it will appear in the developer options screen to be able to enable it and test it. + * If true: the feature is finished, it will not appear in the developer options screen. + */ + val isFinished: Boolean } diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt index 9bb6d2f862..59ec586004 100644 --- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt @@ -25,52 +25,61 @@ enum class FeatureFlags( override val key: String, override val title: String, override val description: String? = null, - override val defaultValue: Boolean + override val defaultValue: Boolean, + override val isFinished: Boolean, ) : Feature { LocationSharing( key = "feature.locationsharing", title = "Allow user to share location", defaultValue = true, + isFinished = true, ), Polls( key = "feature.polls", title = "Polls", description = "Create poll and render poll events in the timeline", defaultValue = true, + isFinished = true, ), NotificationSettings( key = "feature.notificationsettings", title = "Show notification settings", defaultValue = true, + isFinished = true, ), VoiceMessages( key = "feature.voicemessages", title = "Voice messages", description = "Send and receive voice messages", defaultValue = true, + isFinished = true, ), PinUnlock( key = "feature.pinunlock", title = "Pin unlock", description = "Allow user to lock/unlock the app with a pin code or biometrics", defaultValue = true, + isFinished = true, ), Mentions( key = "feature.mentions", title = "Mentions", description = "Type `@` to get mention suggestions and insert them", - defaultValue = false, + defaultValue = true, + isFinished = false, ), SecureStorage( key = "feature.securestorage", title = "Chat backup", description = "Allow access to backup and restore chat history settings", - defaultValue = false, + defaultValue = true, + isFinished = false, ), ReadReceipts( key = "feature.readreceipts", title = "Show read receipts", description = null, - defaultValue = false, + defaultValue = true, + isFinished = false, ), } diff --git a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt index a1a2c3665c..7866ac1f1f 100644 --- a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt +++ b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt @@ -39,9 +39,9 @@ class StaticFeatureFlagProvider @Inject constructor() : FeatureFlags.NotificationSettings -> true FeatureFlags.VoiceMessages -> true FeatureFlags.PinUnlock -> true - FeatureFlags.Mentions -> false - FeatureFlags.SecureStorage -> false - FeatureFlags.ReadReceipts -> false + FeatureFlags.Mentions -> true + FeatureFlags.SecureStorage -> true + FeatureFlags.ReadReceipts -> true } } else { false diff --git a/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt b/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt index 117005fd6f..fc2b605d47 100644 --- a/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt +++ b/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt @@ -37,7 +37,7 @@ class DefaultFeatureFlagServiceTest { fun `given service without provider when set enabled feature is called then it returns false`() = runTest { val featureFlagService = DefaultFeatureFlagService(emptySet()) val result = featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, true) - assertThat(result).isEqualTo(false) + assertThat(result).isFalse() } @Test @@ -45,7 +45,7 @@ class DefaultFeatureFlagServiceTest { val featureFlagProvider = FakeMutableFeatureFlagProvider(0) val featureFlagService = DefaultFeatureFlagService(setOf(featureFlagProvider)) val result = featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, true) - assertThat(result).isEqualTo(true) + assertThat(result).isTrue() } @Test @@ -54,9 +54,9 @@ class DefaultFeatureFlagServiceTest { val featureFlagService = DefaultFeatureFlagService(setOf(featureFlagProvider)) featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, true) featureFlagService.isFeatureEnabledFlow(FeatureFlags.LocationSharing).test { - assertThat(awaitItem()).isEqualTo(true) + assertThat(awaitItem()).isTrue() featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, false) - assertThat(awaitItem()).isEqualTo(false) + assertThat(awaitItem()).isFalse() } } @@ -68,7 +68,7 @@ class DefaultFeatureFlagServiceTest { lowPriorityFeatureFlagProvider.setFeatureEnabled(FeatureFlags.LocationSharing, false) highPriorityFeatureFlagProvider.setFeatureEnabled(FeatureFlags.LocationSharing, true) featureFlagService.isFeatureEnabledFlow(FeatureFlags.LocationSharing).test { - assertThat(awaitItem()).isEqualTo(true) + assertThat(awaitItem()).isTrue() } } } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index f9ac0d84a5..dc452f1514 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -41,7 +41,7 @@ interface MatrixClient : Closeable { val roomListService: RoomListService val mediaLoader: MatrixMediaLoader suspend fun getRoom(roomId: RoomId): MatrixRoom? - suspend fun findDM(userId: UserId): MatrixRoom? + suspend fun findDM(userId: UserId): RoomId? suspend fun ignoreUser(userId: UserId): Result suspend fun unignoreUser(userId: UserId): Result suspend fun createRoom(createRoomParams: CreateRoomParameters): Result diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/BackupUploadState.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/BackupUploadState.kt index 8b5d721b28..7e349216f4 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/BackupUploadState.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/BackupUploadState.kt @@ -16,14 +16,12 @@ package io.element.android.libraries.matrix.api.encryption +import androidx.compose.runtime.Immutable + +@Immutable sealed interface BackupUploadState { data object Unknown : BackupUploadState - data class CheckingIfUploadNeeded( - val backedUpCount: Int, - val totalCount: Int, - ) : BackupUploadState - data object Waiting : BackupUploadState data class Uploading( diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EnableRecoveryProgress.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EnableRecoveryProgress.kt index 1ee85a1cc1..ed45293c49 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EnableRecoveryProgress.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EnableRecoveryProgress.kt @@ -17,9 +17,10 @@ package io.element.android.libraries.matrix.api.encryption sealed interface EnableRecoveryProgress { - data object Unknown : EnableRecoveryProgress - data object CreatingRecoveryKey : EnableRecoveryProgress + data object Starting : EnableRecoveryProgress data object CreatingBackup : EnableRecoveryProgress + data object CreatingRecoveryKey : EnableRecoveryProgress data class BackingUp(val backedUpCount: Int, val totalCount: Int) : EnableRecoveryProgress + data object RoomKeyUploadError : EnableRecoveryProgress data class Done(val recoveryKey: String) : EnableRecoveryProgress } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt index 534f5b68e2..522445b6c2 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt @@ -43,9 +43,9 @@ interface EncryptionService { suspend fun doesBackupExistOnServer(): Result /** - * Note: accept bot recoveryKey and passphrase. + * Note: accept both recoveryKey and passphrase. */ - suspend fun fixRecoveryIssues(recoveryKey: String): Result + suspend fun recover(recoveryKey: String): Result /** * Wait for backup upload steady state. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemGroupPositionProvider.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/RecoveryException.kt similarity index 56% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemGroupPositionProvider.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/RecoveryException.kt index 8d8d1231ec..fae2dad083 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemGroupPositionProvider.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/RecoveryException.kt @@ -14,15 +14,12 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.timeline.model +package io.element.android.libraries.matrix.api.encryption -import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.matrix.api.exception.ClientException -internal class TimelineItemGroupPositionProvider : PreviewParameterProvider { - override val values = sequenceOf( - TimelineItemGroupPosition.First, - TimelineItemGroupPosition.Middle, - TimelineItemGroupPosition.Last, - TimelineItemGroupPosition.None, - ) +sealed class RecoveryException(message: String) : Exception(message) { + class SecretStorage(message: String) : RecoveryException(message) + data object BackupExistsOnServer : RecoveryException("BackupExistsOnServer") + data class Client(val exception: ClientException) : RecoveryException(exception.message ?: "Unknown error") } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/SteadyStateException.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/SteadyStateException.kt index 1e83344a02..88525caac2 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/SteadyStateException.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/SteadyStateException.kt @@ -16,6 +16,9 @@ package io.element.android.libraries.matrix.api.encryption +import androidx.compose.runtime.Immutable + +@Immutable sealed interface SteadyStateException { /** * The backup can be deleted. diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/AudioDetails.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/AudioDetails.kt index 059369c5da..d3ff0e9f83 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/AudioDetails.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/AudioDetails.kt @@ -16,9 +16,10 @@ package io.element.android.libraries.matrix.api.media -import java.time.Duration +import kotlinx.collections.immutable.ImmutableList +import kotlin.time.Duration data class AudioDetails( val duration: Duration, - val waveform: List, + val waveform: ImmutableList, ) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/AudioInfo.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/AudioInfo.kt index bd4539bced..542c13f75c 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/AudioInfo.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/AudioInfo.kt @@ -16,7 +16,7 @@ package io.element.android.libraries.matrix.api.media -import java.time.Duration +import kotlin.time.Duration data class AudioInfo( val duration: Duration?, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/VideoInfo.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/VideoInfo.kt index b7af54c6b2..53cb2564e1 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/VideoInfo.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/VideoInfo.kt @@ -16,7 +16,7 @@ package io.element.android.libraries.matrix.api.media -import java.time.Duration +import kotlin.time.Duration data class VideoInfo( val duration: Duration?, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/mxc/MxcTools.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/mxc/MxcTools.kt new file mode 100644 index 0000000000..43a78d1d23 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/mxc/MxcTools.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrix.api.mxc + +import javax.inject.Inject + +class MxcTools @Inject constructor() { + /** + * Regex to match a Matrix Content (mxc://) URI. + * + * See: https://spec.matrix.org/v1.8/client-server-api/#matrix-content-mxc-uris + */ + private val mxcRegex = Regex("""^mxc://([^/]+)/([^/]+)$""") + + /** + * Sanitizes an mxcUri to be used as a relative file path. + * + * @param mxcUri the Matrix Content (mxc://) URI of the file. + * @return the relative file path as "/" or null if the mxcUri is invalid. + */ + fun mxcUri2FilePath(mxcUri: String): String? = mxcRegex.matchEntire(mxcUri)?.let { match -> + buildString { + append(match.groupValues[1]) + append("/") + append(match.groupValues[2]) + } + } +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt index 1abbf80130..8275b8ee5f 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt @@ -25,6 +25,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.MessageType data class NotificationData( val eventId: EventId, val roomId: RoomId, + // mxc url val senderAvatarUrl: String?, val senderDisplayName: String?, val roomAvatarUrl: String?, @@ -34,8 +35,7 @@ data class NotificationData( val isNoisy: Boolean, val timestamp: Long, val content: NotificationContent, - // For images for instance - val contentUrl: String?, + val hasMention: Boolean, ) sealed interface NotificationContent { diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt index 71d46a2b8e..f1fe18d6e6 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt @@ -38,5 +38,8 @@ interface NotificationSettingsService { suspend fun setRoomMentionEnabled(enabled: Boolean): Result suspend fun isCallEnabled(): Result suspend fun setCallEnabled(enabled: Boolean): Result + suspend fun isInviteForMeEnabled(): Result + suspend fun setInviteForMeEnabled(enabled: Boolean): Result suspend fun getRoomsWithUserDefinedRules(): Result> + suspend fun canHomeServerPushEncryptedEventsToDevice(): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt index 72caa711cb..1fc350775d 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt @@ -17,19 +17,31 @@ package io.element.android.libraries.matrix.api.permalink import android.net.Uri +import androidx.compose.runtime.Immutable +import io.element.android.libraries.matrix.api.core.RoomId +import kotlinx.collections.immutable.ImmutableList /** * This sealed class represents all the permalink cases. * You don't have to instantiate yourself but should use [PermalinkParser] instead. */ +@Immutable sealed interface PermalinkData { data class RoomLink( val roomIdOrAlias: String, val isRoomAlias: Boolean, val eventId: String?, - val viaParameters: List - ) : PermalinkData + val viaParameters: ImmutableList + ) : PermalinkData { + fun getRoomId(): RoomId? { + return roomIdOrAlias.takeIf { !isRoomAlias }?.let(::RoomId) + } + + fun getRoomAlias(): String? { + return roomIdOrAlias.takeIf { isRoomAlias } + } + } /* * &room_name=Team2 diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt index 4a89d05276..dcd5221de8 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt @@ -19,6 +19,7 @@ package io.element.android.libraries.matrix.api.permalink import android.net.Uri import android.net.UrlQuerySanitizer import io.element.android.libraries.matrix.api.core.MatrixPatterns +import kotlinx.collections.immutable.toImmutableList import timber.log.Timber import java.net.URLDecoder @@ -80,7 +81,7 @@ object PermalinkParser { roomIdOrAlias = decodedIdentifier, isRoomAlias = true, eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) }, - viaParameters = viaQueryParameters + viaParameters = viaQueryParameters.toImmutableList() ) } else -> PermalinkData.FallbackLink(uri, MatrixPatterns.isGroupId(identifier)) @@ -119,7 +120,7 @@ object PermalinkParser { roomIdOrAlias = identifier, isRoomAlias = false, eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) }, - viaParameters = viaQueryParameters + viaParameters = viaQueryParameters.toImmutableList() ) } } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt index e9ad35dafa..00364d4822 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt @@ -100,9 +100,9 @@ interface MatrixRoom : Closeable { suspend fun redactEvent(eventId: EventId, reason: String? = null): Result - suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo, progressCallback: ProgressCallback?): Result + suspend fun sendImage(file: File, thumbnailFile: File?, imageInfo: ImageInfo, progressCallback: ProgressCallback?): Result - suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo, progressCallback: ProgressCallback?): Result + suspend fun sendVideo(file: File, thumbnailFile: File?, videoInfo: VideoInfo, progressCallback: ProgressCallback?): Result suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result @@ -132,6 +132,9 @@ interface MatrixRoom : Closeable { suspend fun canUserTriggerRoomNotification(userId: UserId): Result + suspend fun canUserJoinCall(userId: UserId): Result = + canUserSendState(userId, StateEventType.CALL_MEMBER) + suspend fun updateAvatar(mimeType: String, data: ByteArray): Result suspend fun removeAvatar(): Result @@ -176,6 +179,23 @@ interface MatrixRoom : Closeable { pollKind: PollKind, ): Result + /** + * Edit a poll in the room. + * + * @param pollStartId The event ID of the poll start event. + * @param question The question to ask. + * @param answers The list of answers. + * @param maxSelections The maximum number of answers that can be selected. + * @param pollKind The kind of poll to create. + */ + suspend fun editPoll( + pollStartId: EventId, + question: String, + answers: List, + maxSelections: Int, + pollKind: PollKind, + ): Result + /** * Send a response to a poll. * @@ -221,5 +241,7 @@ interface MatrixRoom : Closeable { */ fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result + fun pollHistory(): MatrixTimeline + override fun close() = destroy() } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt index 6690387b04..1304ca40a8 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt @@ -16,8 +16,11 @@ package io.element.android.libraries.matrix.api.room +import androidx.compose.runtime.Immutable import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem +import kotlinx.collections.immutable.ImmutableList +@Immutable data class MatrixRoomInfo( val id: String, val name: String?, @@ -28,7 +31,7 @@ data class MatrixRoomInfo( val isSpace: Boolean, val isTombstoned: Boolean, val canonicalAlias: String?, - val alternativeAliases: List, + val alternativeAliases: ImmutableList, val currentUserMembership: CurrentUserMembership, val latestEvent: EventTimelineItem?, val inviter: RoomMember?, @@ -39,5 +42,5 @@ data class MatrixRoomInfo( val notificationCount: Long, val userDefinedNotificationMode: RoomNotificationMode?, val hasRoomCall: Boolean, - val activeRoomCallParticipants: List + val activeRoomCallParticipants: ImmutableList ) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomMembersState.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomMembersState.kt index 5597aaf1c5..9a25aaa12e 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomMembersState.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomMembersState.kt @@ -17,13 +17,14 @@ package io.element.android.libraries.matrix.api.room import androidx.compose.runtime.Immutable +import kotlinx.collections.immutable.ImmutableList @Immutable sealed interface MatrixRoomMembersState { data object Unknown : MatrixRoomMembersState - data class Pending(val prevRoomMembers: List? = null) : MatrixRoomMembersState - data class Error(val failure: Throwable, val prevRoomMembers: List? = null) : MatrixRoomMembersState - data class Ready(val roomMembers: List) : MatrixRoomMembersState + data class Pending(val prevRoomMembers: ImmutableList? = null) : MatrixRoomMembersState + data class Error(val failure: Throwable, val prevRoomMembers: ImmutableList? = null) : MatrixRoomMembersState + data class Ready(val roomMembers: ImmutableList) : MatrixRoomMembersState } fun MatrixRoomMembersState.roomMembers(): List? { diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/Mention.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/Mention.kt index 30aba3c3c0..6a1b1f60ef 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/Mention.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/Mention.kt @@ -16,7 +16,11 @@ package io.element.android.libraries.matrix.api.room +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.UserId + sealed interface Mention { - data class User(val userId: String): Mention + data class User(val userId: UserId): Mention data object AtRoom: Mention + data class Room(val roomId: RoomId?, val roomAlias: String?): Mention } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt index 3c9bd030b0..54c00572c5 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt @@ -32,3 +32,7 @@ data class RoomMember( enum class RoomMembershipState { BAN, INVITE, JOIN, KNOCK, LEAVE } + +fun RoomMember.getBestName(): String { + return displayName?.takeIf { it.isNotEmpty() } ?: userId.value +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/StartDM.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/StartDM.kt new file mode 100644 index 0000000000..7d0fd9a582 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/StartDM.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrix.api.room + +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.UserId + +/** + * Try to find an existing DM with the given user, or create one if none exists. + */ +suspend fun MatrixClient.startDM(userId: UserId): StartDMResult { + val existingDM = findDM(userId) + return if (existingDM != null) { + StartDMResult.Success(existingDM, isNew = false) + } else { + createDM(userId).fold( + { StartDMResult.Success(it, isNew = true) }, + { StartDMResult.Failure(it) } + ) + } +} + +sealed interface StartDMResult { + data class Success(val roomId: RoomId, val isNew: Boolean) : StartDMResult + data class Failure(val throwable: Throwable) : StartDMResult +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/StateEventType.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/StateEventType.kt index 50cde59b37..3cfacf4a23 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/StateEventType.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/StateEventType.kt @@ -20,6 +20,7 @@ enum class StateEventType { POLICY_RULE_ROOM, POLICY_RULE_SERVER, POLICY_RULE_USER, + CALL_MEMBER, ROOM_ALIASES, ROOM_AVATAR, ROOM_CANONICAL_ALIAS, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/DynamicRoomList.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/DynamicRoomList.kt new file mode 100644 index 0000000000..1f46c2780d --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/DynamicRoomList.kt @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrix.api.roomlist + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach + +/** + * RoomList with dynamic filtering and loading. + * This is useful for large lists of rooms. + * It lets load rooms on demand and filter them. + */ +interface DynamicRoomList : RoomList { + + sealed interface Filter { + /** + * No filter applied. + */ + data object All : Filter + + /** + * Filter only the left rooms. + */ + data object AllNonLeft : Filter + + /** + * Filter all rooms. + */ + data object None : Filter + + /** + * Filter rooms by normalized room name. + */ + data class NormalizedMatchRoomName(val pattern: String) : Filter + } + + val currentFilter: StateFlow + val loadedPages: StateFlow + val pageSize: Int + + /** + * Load more rooms into the list if possible. + */ + suspend fun loadMore() + + /** + * Reset the list to its initial size. + */ + suspend fun reset() + + /** + * Update the filter to apply to the list. + * @param filter the filter to apply. + */ + suspend fun updateFilter(filter: Filter) +} + +/** + * Offers a way to load all the rooms incrementally. + * It will load more room until all are loaded. + * If total number of rooms increase, it will load more pages if needed. + * The number of rooms is independent of the filter. + */ +fun DynamicRoomList.loadAllIncrementally(coroutineScope: CoroutineScope) { + combine( + loadedPages, + loadingState, + ) { loadedPages, loadingState -> + loadedPages to loadingState + } + .onEach { (loadedPages, loadingState) -> + when (loadingState) { + is RoomList.LoadingState.Loaded -> { + if (pageSize * loadedPages < loadingState.numberOfRooms) { + loadMore() + } + } + RoomList.LoadingState.NotLoaded -> Unit + } + } + .launchIn(coroutineScope) +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt index 3d429766b0..8722e2c5bd 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt @@ -28,6 +28,7 @@ import kotlin.time.Duration * Can be retrieved from [RoomListService] methods. */ interface RoomList { + sealed interface LoadingState { data object NotLoaded : LoadingState data class Loaded(val numberOfRooms: Int) : LoadingState @@ -43,6 +44,12 @@ interface RoomList { * This is useful to know if a specific set of rooms is loaded or not. */ val loadingState: StateFlow + + /** + * Force a refresh of the room summaries. + * Might be useful for some situations where we are not notified of changes. + */ + suspend fun rebuildSummaries() } suspend fun RoomList.awaitLoaded(timeout: Duration = Duration.INFINITE) { diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt index 3bd445a282..5682a43389 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt @@ -16,6 +16,7 @@ package io.element.android.libraries.matrix.api.roomlist +import androidx.compose.runtime.Immutable import kotlinx.coroutines.flow.StateFlow /** @@ -25,6 +26,7 @@ import kotlinx.coroutines.flow.StateFlow */ interface RoomListService { + @Immutable sealed interface State { data object Idle : State data object Running : State @@ -32,21 +34,22 @@ interface RoomListService { data object Terminated : State } + @Immutable sealed interface SyncIndicator { data object Show : SyncIndicator data object Hide : SyncIndicator } /** - * returns a [RoomList] object of all rooms we want to display. + * returns a [DynamicRoomList] object of all rooms we want to display. * This will exclude some rooms like the invites, or spaces. */ - fun allRooms(): RoomList + val allRooms: DynamicRoomList /** * returns a [RoomList] object of all invites. */ - fun invites(): RoomList + val invites: RoomList /** * Will set the visible range of all rooms. @@ -54,11 +57,6 @@ interface RoomListService { */ fun updateAllRoomsVisibleRange(range: IntRange) - /** - * Rebuild the room summaries, required when we know some data may have changed. (E.g. room notification settings) - */ - fun rebuildRoomSummaries() - /** * The sync indicator as a flow. */ diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimeline.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimeline.kt index 5fa55c357f..2c22b80246 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimeline.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimeline.kt @@ -20,7 +20,7 @@ import io.element.android.libraries.matrix.api.core.EventId import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow -interface MatrixTimeline { +interface MatrixTimeline : AutoCloseable { data class PaginationState( val isBackPaginating: Boolean, @@ -28,13 +28,21 @@ interface MatrixTimeline { val beginningOfRoomReached: Boolean, ) { val canBackPaginate = !isBackPaginating && hasMoreToLoadBackwards + + companion object { + val Initial = PaginationState( + isBackPaginating = false, + hasMoreToLoadBackwards = true, + beginningOfRoomReached = false + ) + } } val paginationState: StateFlow val timelineItems: Flow> + suspend fun paginateBackwards(requestSize: Int): Result suspend fun paginateBackwards(requestSize: Int, untilNumberOfItems: Int): Result suspend fun fetchDetailsForEvent(eventId: EventId): Result - - suspend fun sendReadReceipt(eventId: EventId): Result + suspend fun sendReadReceipt(eventId: EventId, receiptType: ReceiptType): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimelineItem.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimelineItem.kt index fe328a57d2..ae4e07a8f4 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimelineItem.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimelineItem.kt @@ -22,12 +22,12 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventTimeline import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem sealed interface MatrixTimelineItem { - data class Event(val uniqueId: Long, val event: EventTimelineItem) : MatrixTimelineItem { + data class Event(val uniqueId: String, val event: EventTimelineItem) : MatrixTimelineItem { val eventId: EventId? = event.eventId val transactionId: TransactionId? = event.transactionId } - data class Virtual(val uniqueId: Long, val virtual: VirtualTimelineItem) : MatrixTimelineItem + data class Virtual(val uniqueId: String, val virtual: VirtualTimelineItem) : MatrixTimelineItem data object Other : MatrixTimelineItem } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/ReceiptType.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/ReceiptType.kt new file mode 100644 index 0000000000..7f02285b7e --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/ReceiptType.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrix.api.timeline + +enum class ReceiptType { + READ, + READ_PRIVATE, + FULLY_READ; +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt index f63953e260..5dcfd88016 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt @@ -16,11 +16,15 @@ package io.element.android.libraries.matrix.api.timeline.item.event +import androidx.compose.runtime.Immutable import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.media.ImageInfo import io.element.android.libraries.matrix.api.poll.PollAnswer import io.element.android.libraries.matrix.api.poll.PollKind +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.ImmutableMap +@Immutable sealed interface EventContent data class MessageContent( @@ -43,14 +47,17 @@ data class PollContent( val question: String, val kind: PollKind, val maxSelections: ULong, - val answers: List, - val votes: Map>, - val endTime: ULong? + val answers: ImmutableList, + val votes: ImmutableMap>, + val endTime: ULong?, + val isEdited: Boolean, ) : EventContent data class UnableToDecryptContent( val data: Data ) : EventContent { + + @Immutable sealed interface Data { data class OlmV1Curve25519AesSha2( val senderKey: String diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventReaction.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventReaction.kt index a2e68d17d2..65fc5a06a9 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventReaction.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventReaction.kt @@ -16,7 +16,11 @@ package io.element.android.libraries.matrix.api.timeline.item.event +import androidx.compose.runtime.Immutable +import kotlinx.collections.immutable.ImmutableList + +@Immutable data class EventReaction( val key: String, - val senders: List + val senders: ImmutableList ) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt index 9f38ce9441..fa15f8f096 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt @@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo +import kotlinx.collections.immutable.ImmutableList data class EventTimelineItem( val eventId: EventId?, @@ -29,8 +30,8 @@ data class EventTimelineItem( val isOwn: Boolean, val isRemote: Boolean, val localSendState: LocalEventSendState?, - val reactions: List, - val receipts: List, + val reactions: ImmutableList, + val receipts: ImmutableList, val sender: UserId, val senderProfile: ProfileTimelineDetails, val timestamp: Long, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/InReplyTo.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/InReplyTo.kt index 14a84e2a90..6965ed9f1e 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/InReplyTo.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/InReplyTo.kt @@ -16,9 +16,11 @@ package io.element.android.libraries.matrix.api.timeline.item.event +import androidx.compose.runtime.Immutable import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId +@Immutable sealed interface InReplyTo { /** The event details are not loaded yet. We can fetch them. */ data class NotLoaded(val eventId: EventId) : InReplyTo diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/LocalEventSendState.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/LocalEventSendState.kt index 265be8af79..f74ddca3a8 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/LocalEventSendState.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/LocalEventSendState.kt @@ -16,8 +16,10 @@ package io.element.android.libraries.matrix.api.timeline.item.event +import androidx.compose.runtime.Immutable import io.element.android.libraries.matrix.api.core.EventId +@Immutable sealed interface LocalEventSendState { data object NotSentYet : LocalEventSendState data object Canceled : LocalEventSendState diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageType.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageType.kt index c8122935bd..0b2d948c4e 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageType.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageType.kt @@ -16,6 +16,7 @@ package io.element.android.libraries.matrix.api.timeline.item.event +import androidx.compose.runtime.Immutable import io.element.android.libraries.matrix.api.media.AudioDetails import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.media.FileInfo @@ -23,10 +24,9 @@ import io.element.android.libraries.matrix.api.media.ImageInfo import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.media.VideoInfo +@Immutable sealed interface MessageType -data object UnknownMessageType : MessageType - data class EmoteMessageType( val body: String, val formatted: FormattedBody? diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/OtherState.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/OtherState.kt index 2cbfaf76b4..6960b3565d 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/OtherState.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/OtherState.kt @@ -16,6 +16,9 @@ package io.element.android.libraries.matrix.api.timeline.item.event +import androidx.compose.runtime.Immutable + +@Immutable sealed interface OtherState { data object PolicyRuleRoom : OtherState data object PolicyRuleServer : OtherState diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ProfileTimelineDetails.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ProfileTimelineDetails.kt index eddb9eb169..44664a256a 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ProfileTimelineDetails.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ProfileTimelineDetails.kt @@ -16,6 +16,9 @@ package io.element.android.libraries.matrix.api.timeline.item.event +import androidx.compose.runtime.Immutable + +@Immutable sealed interface ProfileTimelineDetails { data object Unavailable : ProfileTimelineDetails diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/user/MatrixSearchUserResults.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/user/MatrixSearchUserResults.kt index a63a31b51f..a7985c864f 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/user/MatrixSearchUserResults.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/user/MatrixSearchUserResults.kt @@ -16,7 +16,9 @@ package io.element.android.libraries.matrix.api.user +import kotlinx.collections.immutable.ImmutableList + data class MatrixSearchUserResults( - val results: List, + val results: ImmutableList, val limited: Boolean, ) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationData.kt new file mode 100644 index 0000000000..9368773ae8 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationData.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrix.api.verification + +import androidx.compose.runtime.Immutable + +@Immutable +sealed interface SessionVerificationData { + data class Emojis( + // 7 emojis + val emojis: List, + ) : SessionVerificationData + + data class Decimals( + // 3 numbers + val decimals: List, + ) : SessionVerificationData +} + +// https://spec.matrix.org/unstable/client-server-api/#sas-method-emoji +data class VerificationEmoji( + val number: Int, + val emoji: String, + val description: String, +) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationService.kt index c463530050..db8fa1a939 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationService.kt @@ -16,6 +16,7 @@ package io.element.android.libraries.matrix.api.verification +import androidx.compose.runtime.Immutable import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow @@ -24,7 +25,7 @@ interface SessionVerificationService { /** * State of the current verification flow ([VerificationFlowState.Initial] if not started). */ - val verificationFlowState : StateFlow + val verificationFlowState: StateFlow /** * The internal service that checks verification can only run after the initial sync. @@ -75,6 +76,7 @@ interface SessionVerificationService { } /** Verification status of the current session. */ +@Immutable sealed interface SessionVerifiedStatus { /** Unknown status, we couldn't read the actual value from the SDK. */ data object Unknown : SessionVerifiedStatus @@ -87,6 +89,7 @@ sealed interface SessionVerifiedStatus { } /** States produced by the [SessionVerificationService]. */ +@Immutable sealed interface VerificationFlowState { /** Initial state. */ data object Initial : VerificationFlowState @@ -97,8 +100,8 @@ sealed interface VerificationFlowState { /** Short Authentication String (SAS) verification started between the 2 devices. */ data object StartedSasVerification : VerificationFlowState - /** Verification data for the SAS verification (emojis) received. */ - data class ReceivedVerificationData(val emoji: List) : VerificationFlowState + /** Verification data for the SAS verification received. */ + data class ReceivedVerificationData(val data: SessionVerificationData) : VerificationFlowState /** Verification completed successfully. */ data object Finished : VerificationFlowState diff --git a/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/mxc/MxcToolsTest.kt b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/mxc/MxcToolsTest.kt new file mode 100644 index 0000000000..652b5a6de6 --- /dev/null +++ b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/mxc/MxcToolsTest.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrix.api.mxc + +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +class MxcToolsTest { + @Test + fun `mxcUri2FilePath returns extracted path`() { + val mxcTools = MxcTools() + val mxcUri = "mxc://server.org/abc123" + val filePath = mxcTools.mxcUri2FilePath(mxcUri) + assertThat(filePath).isEqualTo("server.org/abc123") + } + + @Test + fun `mxcUri2FilePath returns null for invalid data`() { + val mxcTools = MxcTools() + assertThat(mxcTools.mxcUri2FilePath("")).isNull() + assertThat(mxcTools.mxcUri2FilePath("mxc://server.org")).isNull() + assertThat(mxcTools.mxcUri2FilePath("mxc://server.org/")).isNull() + assertThat(mxcTools.mxcUri2FilePath("m://server.org/abc123")).isNull() + } +} diff --git a/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkDataTest.kt b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkDataTest.kt new file mode 100644 index 0000000000..08955411d8 --- /dev/null +++ b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkDataTest.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrix.api.permalink + +import com.google.common.truth.Truth.assertThat +import kotlinx.collections.immutable.persistentListOf +import org.junit.Test + +class PermalinkDataTest { + + @Test + fun `getRoomId() returns value when isRoomAlias is false`() { + val permalinkData = PermalinkData.RoomLink( + roomIdOrAlias = "!abcdef123456:matrix.org", + isRoomAlias = false, + eventId = null, + viaParameters = persistentListOf(), + ) + assertThat(permalinkData.getRoomId()).isNotNull() + assertThat(permalinkData.getRoomAlias()).isNull() + } + + @Test + fun `getRoomAlias() returns value when isRoomAlias is true`() { + val permalinkData = PermalinkData.RoomLink( + roomIdOrAlias = "#room:matrix.org", + isRoomAlias = true, + eventId = null, + viaParameters = persistentListOf(), + ) + assertThat(permalinkData.getRoomId()).isNull() + assertThat(permalinkData.getRoomAlias()).isNotNull() + } + +} diff --git a/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParserTest.kt b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParserTest.kt index d55b6aebbe..590be150c4 100644 --- a/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParserTest.kt +++ b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParserTest.kt @@ -17,6 +17,7 @@ package io.element.android.libraries.matrix.api.permalink import com.google.common.truth.Truth.assertThat +import kotlinx.collections.immutable.persistentListOf import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -66,7 +67,7 @@ class PermalinkParserTest { roomIdOrAlias = "!aBCD1234:matrix.org", isRoomAlias = false, eventId = null, - viaParameters = emptyList(), + viaParameters = persistentListOf(), ) ) } @@ -79,7 +80,7 @@ class PermalinkParserTest { roomIdOrAlias = "!aBCD1234:matrix.org", isRoomAlias = false, eventId = "\$1234567890abcdef:matrix.org", - viaParameters = emptyList(), + viaParameters = persistentListOf(), ) ) } @@ -92,7 +93,7 @@ class PermalinkParserTest { roomIdOrAlias = "!aBCD1234:matrix.org", isRoomAlias = false, eventId = null, - viaParameters = emptyList(), + viaParameters = persistentListOf(), ) ) } @@ -105,7 +106,7 @@ class PermalinkParserTest { roomIdOrAlias = "!aBCD1234:matrix.org", isRoomAlias = false, eventId = "\$1234567890abcdef:matrix.org", - viaParameters = listOf("matrix.org", "matrix.com"), + viaParameters = persistentListOf("matrix.org", "matrix.com"), ) ) } @@ -118,7 +119,7 @@ class PermalinkParserTest { roomIdOrAlias = "#element-android:matrix.org", isRoomAlias = true, eventId = null, - viaParameters = emptyList(), + viaParameters = persistentListOf(), ) ) } diff --git a/libraries/matrix/impl/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts index 81b5f70a11..41c84a0384 100644 --- a/libraries/matrix/impl/build.gradle.kts +++ b/libraries/matrix/impl/build.gradle.kts @@ -44,9 +44,10 @@ dependencies { api(projects.libraries.matrix.api) implementation(libs.dagger) implementation(projects.libraries.core) - implementation("net.java.dev.jna:jna:5.13.0@aar") + implementation("net.java.dev.jna:jna:5.14.0@aar") implementation(libs.androidx.datastore.preferences) implementation(libs.serialization.json) + implementation(libs.kotlinx.collections.immutable) testImplementation(libs.test.junit) testImplementation(libs.test.truth) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 8429968af3..f7c157d2ba 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -53,6 +53,7 @@ import io.element.android.libraries.matrix.impl.room.MatrixRoomInfoMapper import io.element.android.libraries.matrix.impl.room.RoomContentForwarder import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber import io.element.android.libraries.matrix.impl.room.RustMatrixRoom +import io.element.android.libraries.matrix.impl.roomlist.RoomListFactory import io.element.android.libraries.matrix.impl.roomlist.RustRoomListService import io.element.android.libraries.matrix.impl.roomlist.roomOrNull import io.element.android.libraries.matrix.impl.sync.RustSyncService @@ -76,6 +77,7 @@ import org.matrix.rustcomponents.sdk.BackupState import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.ClientDelegate import org.matrix.rustcomponents.sdk.NotificationProcessSetup +import org.matrix.rustcomponents.sdk.PowerLevels import org.matrix.rustcomponents.sdk.Room import org.matrix.rustcomponents.sdk.RoomListItem import org.matrix.rustcomponents.sdk.TaskHandle @@ -89,7 +91,7 @@ import org.matrix.rustcomponents.sdk.RoomVisibility as RustRoomVisibility import org.matrix.rustcomponents.sdk.SyncService as ClientSyncService @OptIn(ExperimentalCoroutinesApi::class) -class RustMatrixClient constructor( +class RustMatrixClient( private val client: Client, private val syncService: ClientSyncService, private val sessionStore: SessionStore, @@ -117,10 +119,9 @@ class RustMatrixClient constructor( .filterByPushRules() .finish() } - private val notificationSettings = client.getNotificationSettings() - private val notificationService = RustNotificationService(sessionId, notificationClient, dispatchers, clock) - private val notificationSettingsService = RustNotificationSettingsService(notificationSettings, dispatchers) + private val notificationSettingsService = RustNotificationSettingsService(client, dispatchers) + .apply { start() } private val roomSyncSubscriber = RoomSyncSubscriber(innerRoomListService, dispatchers) private val encryptionService = RustEncryptionService( client = client, @@ -171,7 +172,11 @@ class RustMatrixClient constructor( RustRoomListService( innerRoomListService = innerRoomListService, sessionCoroutineScope = sessionCoroutineScope, - dispatcher = sessionDispatcher, + roomListFactory = RoomListFactory( + innerRoomListService = innerRoomListService, + coroutineScope = sessionCoroutineScope, + dispatcher = sessionDispatcher, + ), ) override val roomListService: RoomListService @@ -200,7 +205,7 @@ class RustMatrixClient constructor( var cachedPairOfRoom = pairOfRoom(roomId) if (cachedPairOfRoom == null) { //... otherwise, lets wait for the SS to load all rooms and check again. - roomListService.allRooms().awaitLoaded() + roomListService.allRooms.awaitLoaded() cachedPairOfRoom = pairOfRoom(roomId) } cachedPairOfRoom?.let { (roomListItem, fullRoom) -> @@ -209,6 +214,7 @@ class RustMatrixClient constructor( isKeyBackupEnabled = client.encryption().backupState() == BackupState.ENABLED, roomListItem = roomListItem, innerRoom = fullRoom, + innerTimeline = fullRoom.timeline(), roomNotificationSettingsService = notificationSettingsService, sessionCoroutineScope = sessionCoroutineScope, coroutineDispatchers = dispatchers, @@ -234,9 +240,8 @@ class RustMatrixClient constructor( } } - override suspend fun findDM(userId: UserId): MatrixRoom? { - val roomId = client.getDmRoom(userId.value)?.use { RoomId(it.id()) } - return roomId?.let { getRoom(it) } + override suspend fun findDM(userId: UserId): RoomId? { + return client.getDmRoom(userId.value)?.use { RoomId(it.id()) } } override suspend fun ignoreUser(userId: UserId): Result = withContext(sessionDispatcher) { @@ -269,12 +274,13 @@ class RustMatrixClient constructor( }, invite = createRoomParams.invite?.map { it.value }, avatar = createRoomParams.avatar, + powerLevelContentOverride = defaultRoomCreationPowerLevels, ) val roomId = RoomId(client.createRoom(rustParams)) // Wait to receive the room back from the sync withTimeout(30_000L) { - roomListService.allRooms().summaries + roomListService.allRooms.summaries .filter { roomSummaries -> roomSummaries.map { it.identifier() }.contains(roomId.value) } @@ -291,7 +297,7 @@ class RustMatrixClient constructor( isDirect = true, visibility = RoomVisibility.PRIVATE, preset = RoomPreset.TRUSTED_PRIVATE_CHAT, - invite = listOf(userId) + invite = listOf(userId), ) return createRoom(createRoomParams) } @@ -339,8 +345,7 @@ class RustMatrixClient constructor( override fun close() { sessionCoroutineScope.cancel() clientDelegateTaskHandle?.cancelAndDestroy() - notificationSettings.setDelegate(null) - notificationSettings.destroy() + notificationSettingsService.destroy() verificationService.destroy() syncService.destroy() innerRoomListService.destroy() @@ -476,3 +481,18 @@ class RustMatrixClient constructor( } } +private val defaultRoomCreationPowerLevels = PowerLevels( + usersDefault = null, + eventsDefault = null, + stateDefault = null, + ban = null, + kick = null, + redact = null, + invite = null, + notifications = null, + users = mapOf(), + events = mapOf( + "m.call.member" to 0, + "org.matrix.msc3401.call.member" to 0, + ) +) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/BackupUploadStateMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/BackupUploadStateMapper.kt index 9ac5330294..e8d90f954f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/BackupUploadStateMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/BackupUploadStateMapper.kt @@ -22,18 +22,22 @@ import org.matrix.rustcomponents.sdk.BackupUploadState as RustBackupUploadState class BackupUploadStateMapper { fun map(rustEnableProgress: RustBackupUploadState): BackupUploadState { return when (rustEnableProgress) { - is RustBackupUploadState.CheckingIfUploadNeeded -> - BackupUploadState.CheckingIfUploadNeeded( - backedUpCount = rustEnableProgress.backedUpCount.toInt(), - totalCount = rustEnableProgress.totalCount.toInt(), - ) RustBackupUploadState.Done -> BackupUploadState.Done - is RustBackupUploadState.Uploading -> - BackupUploadState.Uploading( - backedUpCount = rustEnableProgress.backedUpCount.toInt(), - totalCount = rustEnableProgress.totalCount.toInt(), - ) + is RustBackupUploadState.Uploading -> { + val backedUpCount = rustEnableProgress.backedUpCount.toInt() + val totalCount = rustEnableProgress.totalCount.toInt() + if (backedUpCount == totalCount) { + // Consider that the state is Done in this case, + // the SDK will not send a Done state + BackupUploadState.Done + } else { + BackupUploadState.Uploading( + backedUpCount = backedUpCount, + totalCount = totalCount, + ) + } + } RustBackupUploadState.Waiting -> BackupUploadState.Waiting RustBackupUploadState.Error -> diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/EnableRecoveryProgressMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/EnableRecoveryProgressMapper.kt index a50a68267d..953c86d816 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/EnableRecoveryProgressMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/EnableRecoveryProgressMapper.kt @@ -22,12 +22,14 @@ import org.matrix.rustcomponents.sdk.EnableRecoveryProgress as RustEnableRecover class EnableRecoveryProgressMapper { fun map(rustEnableProgress: RustEnableRecoveryProgress): EnableRecoveryProgress { return when (rustEnableProgress) { - is RustEnableRecoveryProgress.CreatingRecoveryKey -> EnableRecoveryProgress.CreatingRecoveryKey + is RustEnableRecoveryProgress.Starting -> EnableRecoveryProgress.Starting is RustEnableRecoveryProgress.CreatingBackup -> EnableRecoveryProgress.CreatingBackup + is RustEnableRecoveryProgress.CreatingRecoveryKey -> EnableRecoveryProgress.CreatingRecoveryKey is RustEnableRecoveryProgress.BackingUp -> EnableRecoveryProgress.BackingUp( backedUpCount = rustEnableProgress.backedUpCount.toInt(), totalCount = rustEnableProgress.totalCount.toInt(), ) + is RustEnableRecoveryProgress.RoomKeyUploadError -> EnableRecoveryProgress.RoomKeyUploadError is RustEnableRecoveryProgress.Done -> EnableRecoveryProgress.Done( recoveryKey = rustEnableProgress.recoveryKey ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RecoveryExceptionMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RecoveryExceptionMapper.kt new file mode 100644 index 0000000000..b21e035602 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RecoveryExceptionMapper.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrix.impl.encryption + +import io.element.android.libraries.matrix.api.encryption.RecoveryException +import io.element.android.libraries.matrix.api.exception.ClientException +import io.element.android.libraries.matrix.impl.exception.mapClientException +import org.matrix.rustcomponents.sdk.RecoveryException as RustRecoveryException + +fun Throwable.mapRecoveryException(): RecoveryException { + return when (this) { + is RustRecoveryException.SecretStorage -> RecoveryException.SecretStorage( + message = errorMessage + ) + is RustRecoveryException.BackupExistsOnServer -> RecoveryException.BackupExistsOnServer + is RustRecoveryException.Client -> RecoveryException.Client( + source.mapClientException() + ) + else -> RecoveryException.Client( + ClientException.Other("Unknown error") + ) + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt index 30b8c651a0..8732645330 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt @@ -17,6 +17,7 @@ package io.element.android.libraries.matrix.impl.encryption import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.BackupUploadState import io.element.android.libraries.matrix.api.encryption.EnableRecoveryProgress @@ -86,7 +87,7 @@ internal class RustEncryptionService( } }.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, RecoveryState.WAITING_FOR_SYNC) - override val enableRecoveryProgressStateFlow: MutableStateFlow = MutableStateFlow(EnableRecoveryProgress.Unknown) + override val enableRecoveryProgressStateFlow: MutableStateFlow = MutableStateFlow(EnableRecoveryProgress.Starting) fun start() { service.backupStateListener(object : BackupStateListener { @@ -110,6 +111,8 @@ internal class RustEncryptionService( override suspend fun enableBackups(): Result = withContext(dispatchers.io) { runCatching { service.enableBackups() + }.mapFailure { + it.mapRecoveryException() } } @@ -127,6 +130,8 @@ internal class RustEncryptionService( ) // enableRecovery returns the encryption key, but we read it from the state flow .let { } + }.mapFailure { + it.mapRecoveryException() } } @@ -164,24 +169,32 @@ internal class RustEncryptionService( override suspend fun disableRecovery(): Result = withContext(dispatchers.io) { runCatching { service.disableRecovery() + }.mapFailure { + it.mapRecoveryException() } } override suspend fun isLastDevice(): Result = withContext(dispatchers.io) { runCatching { service.isLastDevice() + }.mapFailure { + it.mapRecoveryException() } } override suspend fun resetRecoveryKey(): Result = withContext(dispatchers.io) { runCatching { service.resetRecoveryKey() + }.mapFailure { + it.mapRecoveryException() } } - override suspend fun fixRecoveryIssues(recoveryKey: String): Result = withContext(dispatchers.io) { + override suspend fun recover(recoveryKey: String): Result = withContext(dispatchers.io) { runCatching { - service.fixRecoveryIssues(recoveryKey) + service.recover(recoveryKey) + }.mapFailure { + it.mapRecoveryException() } } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/SteadyStateExceptionMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/SteadyStateExceptionMapper.kt index 331d3f8473..7439f016d8 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/SteadyStateExceptionMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/SteadyStateExceptionMapper.kt @@ -23,13 +23,13 @@ class SteadyStateExceptionMapper { fun map(data: RustSteadyStateException): SteadyStateException { return when (data) { is RustSteadyStateException.BackupDisabled -> SteadyStateException.BackupDisabled( - message = data.message + message = data.message.orEmpty() ) is RustSteadyStateException.Connection -> SteadyStateException.Connection( - message = data.message + message = data.message.orEmpty() ) - is RustSteadyStateException.Laged -> SteadyStateException.Lagged( - message = data.message + is RustSteadyStateException.Lagged -> SteadyStateException.Lagged( + message = data.message.orEmpty() ) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioDetails.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioDetails.kt index 5bca137a85..2bdcf6de0f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioDetails.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioDetails.kt @@ -17,15 +17,18 @@ package io.element.android.libraries.matrix.impl.media import io.element.android.libraries.matrix.api.media.AudioDetails +import kotlinx.collections.immutable.toImmutableList +import kotlin.time.toJavaDuration +import kotlin.time.toKotlinDuration import org.matrix.rustcomponents.sdk.UnstableAudioDetailsContent as RustAudioDetails fun RustAudioDetails.map(): AudioDetails = AudioDetails( - duration = duration, - waveform = waveform.fromMSC3246range(), + duration = duration.toKotlinDuration(), + waveform = waveform.fromMSC3246range().toImmutableList(), ) fun AudioDetails.map(): RustAudioDetails = RustAudioDetails( - duration = duration, + duration = duration.toJavaDuration(), waveform = waveform.toMSC3246range() ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioInfo.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioInfo.kt index 70c3bac6ed..b7cff173be 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioInfo.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioInfo.kt @@ -17,16 +17,18 @@ package io.element.android.libraries.matrix.impl.media import io.element.android.libraries.matrix.api.media.AudioInfo +import kotlin.time.toJavaDuration +import kotlin.time.toKotlinDuration import org.matrix.rustcomponents.sdk.AudioInfo as RustAudioInfo fun RustAudioInfo.map(): AudioInfo = AudioInfo( - duration = duration, + duration = duration?.toKotlinDuration(), size = size?.toLong(), mimetype = mimetype ) fun AudioInfo.map(): RustAudioInfo = RustAudioInfo( - duration = duration, + duration = duration?.toJavaDuration(), size = size?.toULong(), mimetype = mimetype, ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt index fab0146f9d..6ae20e06af 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt @@ -82,7 +82,7 @@ class RustMediaLoader( val mediaFile = innerClient.getMediaFile( mediaSource = mediaSource, body = body, - mimeType = mimeType ?: MimeTypes.OctetStream, + mimeType = mimeType?.takeIf { MimeTypes.hasSubtype(it) } ?: MimeTypes.OctetStream, useCache = useCache, tempDir = cacheDirectory.path, ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/VideoInfo.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/VideoInfo.kt index 661d1b9b33..bfdb328bc9 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/VideoInfo.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/VideoInfo.kt @@ -17,10 +17,12 @@ package io.element.android.libraries.matrix.impl.media import io.element.android.libraries.matrix.api.media.VideoInfo +import kotlin.time.toJavaDuration +import kotlin.time.toKotlinDuration import org.matrix.rustcomponents.sdk.VideoInfo as RustVideoInfo fun RustVideoInfo.map(): VideoInfo = VideoInfo( - duration = duration, + duration = duration?.toKotlinDuration(), height = height?.toLong(), width = width?.toLong(), mimetype = mimetype, @@ -31,7 +33,7 @@ fun RustVideoInfo.map(): VideoInfo = VideoInfo( ) fun VideoInfo.map(): RustVideoInfo = RustVideoInfo( - duration = duration, + duration = duration?.toJavaDuration(), height = height?.toULong(), width = width?.toULong(), mimetype = mimetype, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt index 0d9f794173..6285ba393f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt @@ -52,7 +52,7 @@ class NotificationMapper( isNoisy = item.isNoisy.orFalse(), timestamp = item.timestamp() ?: clock.epochMillis(), content = item.event.use { notificationContentMapper.map(it) }, - contentUrl = null, + hasMention = item.hasMention.orFalse(), ) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt index 42b2662a6d..2eee0a324e 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt @@ -26,16 +26,16 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.withContext -import org.matrix.rustcomponents.sdk.NotificationSettings +import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.NotificationSettingsDelegate import org.matrix.rustcomponents.sdk.NotificationSettingsException import timber.log.Timber class RustNotificationSettingsService( - private val notificationSettings: NotificationSettings, + client: Client, private val dispatchers: CoroutineDispatchers, ) : NotificationSettingsService { - + private val notificationSettings = client.getNotificationSettings() private val _notificationSettingsChangeFlow = MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) override val notificationSettingsChangeFlow: SharedFlow = _notificationSettingsChangeFlow.asSharedFlow() @@ -45,10 +45,15 @@ class RustNotificationSettingsService( } } - init { + fun start() { notificationSettings.setDelegate(notificationSettingsDelegate) } + fun destroy() { + notificationSettings.setDelegate(null) + notificationSettings.destroy() + } + override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result = runCatching { notificationSettings.getRoomNotificationSettings(roomId.value, isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::map) @@ -119,8 +124,25 @@ class RustNotificationSettingsService( } } + override suspend fun isInviteForMeEnabled(): Result = withContext(dispatchers.io) { + runCatching { + notificationSettings.isInviteForMeEnabled() + } + } + + override suspend fun setInviteForMeEnabled(enabled: Boolean): Result = withContext(dispatchers.io) { + runCatching { + notificationSettings.setInviteForMeEnabled(enabled) + } + } + override suspend fun getRoomsWithUserDefinedRules(): Result> = runCatching { notificationSettings.getRoomsWithUserDefinedRules(enabled = true) } + + override suspend fun canHomeServerPushEncryptedEventsToDevice(): Result = + runCatching { + notificationSettings.canHomeserverPushEncryptedEventToDevice() + } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt index c24d996714..3ea1895e22 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt @@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper +import kotlinx.collections.immutable.toImmutableList import org.matrix.rustcomponents.sdk.use import org.matrix.rustcomponents.sdk.Membership as RustMembership import org.matrix.rustcomponents.sdk.RoomInfo as RustRoomInfo @@ -40,7 +41,7 @@ class MatrixRoomInfoMapper( isSpace = it.isSpace, isTombstoned = it.isTombstoned, canonicalAlias = it.canonicalAlias, - alternativeAliases = it.alternativeAliases, + alternativeAliases = it.alternativeAliases.toImmutableList(), currentUserMembership = it.membership.map(), latestEvent = it.latestEvent?.use (timelineItemMapper::map), inviter = it.inviter?.use(RoomMemberMapper::map), @@ -51,7 +52,7 @@ class MatrixRoomInfoMapper( notificationCount = it.notificationCount.toLong(), userDefinedNotificationMode = it.userDefinedNotificationMode?.map(), hasRoomCall = it.hasRoomCall, - activeRoomCallParticipants = it.activeRoomCallParticipants + activeRoomCallParticipants = it.activeRoomCallParticipants.toImmutableList() ) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/Mention.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/Mention.kt index 463496ffe7..795ac2e003 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/Mention.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/Mention.kt @@ -21,6 +21,6 @@ import org.matrix.rustcomponents.sdk.Mentions fun List.map(): Mentions { val hasAtRoom = any { it is Mention.AtRoom } - val userIds = filterIsInstance().map { it.userId } + val userIds = filterIsInstance().map { it.userId.value } return Mentions(userIds, hasAtRoom) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt index 2bfef368a9..f58a5c930d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt @@ -24,8 +24,8 @@ import io.element.android.libraries.matrix.impl.roomlist.roomOrNull import io.element.android.libraries.matrix.impl.timeline.runWithTimelineListenerRegistered import kotlinx.coroutines.CancellationException import kotlinx.coroutines.withTimeout -import org.matrix.rustcomponents.sdk.Room import org.matrix.rustcomponents.sdk.RoomListService +import org.matrix.rustcomponents.sdk.Timeline import kotlin.time.Duration.Companion.milliseconds /** @@ -37,19 +37,19 @@ class RoomContentForwarder( ) { /** - * Forwards the event with the given [eventId] from the [fromRoom] to the given [toRoomIds]. - * @param fromRoom the room to forward the event from + * Forwards the event with the given [eventId] from the [fromTimeline] to the given [toRoomIds]. + * @param fromTimeline the room to forward the event from * @param eventId the id of the event to forward * @param toRoomIds the ids of the rooms to forward the event to * @param timeoutMs the maximum time in milliseconds to wait for the event to be sent to a room */ suspend fun forward( - fromRoom: Room, + fromTimeline: Timeline, eventId: EventId, toRoomIds: List, timeoutMs: Long = 5000L ) { - val content = fromRoom.getTimelineEventContentByEventId(eventId.value) + val content = fromTimeline.getTimelineEventContentByEventId(eventId.value) val targetSlidingSyncRooms = toRoomIds.mapNotNull { roomId -> roomListService.roomOrNull(roomId.value) } val targetRooms = targetSlidingSyncRooms.mapNotNull { slidingSyncRoom -> slidingSyncRoom.use { it.fullRoom() } } val failedForwardingTo = mutableSetOf() @@ -57,9 +57,9 @@ class RoomContentForwarder( room.use { targetRoom -> runCatching { // Sending a message requires a registered timeline listener - targetRoom.runWithTimelineListenerRegistered { + targetRoom.timeline().runWithTimelineListenerRegistered { withTimeout(timeoutMs.milliseconds) { - targetRoom.send(content) + targetRoom.timeline().send(content) } } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 35a61c1a1d..fc7215f583 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -41,6 +41,7 @@ import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.room.roomMembers import io.element.android.libraries.matrix.api.room.roomNotificationSettings +import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings import io.element.android.libraries.matrix.impl.core.toProgressWatcher @@ -50,6 +51,7 @@ import io.element.android.libraries.matrix.impl.media.toMSC3246range import io.element.android.libraries.matrix.impl.notificationsettings.RustNotificationSettingsService import io.element.android.libraries.matrix.impl.poll.toInner import io.element.android.libraries.matrix.impl.room.location.toInner +import io.element.android.libraries.matrix.impl.timeline.AsyncMatrixTimeline import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline import io.element.android.libraries.matrix.impl.util.destroyAll import io.element.android.libraries.matrix.impl.util.mxCallbackFlow @@ -57,6 +59,7 @@ import io.element.android.libraries.matrix.impl.widget.RustWidgetDriver import io.element.android.libraries.matrix.impl.widget.generateWidgetWebViewUrl import io.element.android.libraries.sessionstorage.api.SessionData import io.element.android.services.toolbox.api.systemclock.SystemClock +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -69,7 +72,6 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.EventTimelineItem -import org.matrix.rustcomponents.sdk.Room import org.matrix.rustcomponents.sdk.RoomInfo import org.matrix.rustcomponents.sdk.RoomInfoListener import org.matrix.rustcomponents.sdk.RoomListItem @@ -83,13 +85,16 @@ import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown import org.matrix.rustcomponents.sdk.use import timber.log.Timber import java.io.File +import org.matrix.rustcomponents.sdk.Room as InnerRoom +import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline @OptIn(ExperimentalCoroutinesApi::class) class RustMatrixRoom( override val sessionId: SessionId, - isKeyBackupEnabled: Boolean, + private val isKeyBackupEnabled: Boolean, private val roomListItem: RoomListItem, - private val innerRoom: Room, + private val innerRoom: InnerRoom, + private val innerTimeline: InnerTimeline, private val roomNotificationSettingsService: RustNotificationSettingsService, sessionCoroutineScope: CoroutineScope, private val coroutineDispatchers: CoroutineDispatchers, @@ -127,15 +132,9 @@ class RustMatrixRoom( private val _roomNotificationSettingsStateFlow = MutableStateFlow(MatrixRoomNotificationSettingsState.Unknown) override val roomNotificationSettingsStateFlow: StateFlow = _roomNotificationSettingsStateFlow - override val timeline = RustMatrixTimeline( - isKeyBackupEnabled = isKeyBackupEnabled, - matrixRoom = this, - innerRoom = innerRoom, - roomCoroutineScope = roomCoroutineScope, - dispatcher = roomDispatcher, - lastLoginTimestamp = sessionData.loginTimestamp, - onNewSyncedEvent = { _syncUpdateFlow.value = systemClock.epochMillis() } - ) + override val timeline = createMatrixTimeline(innerTimeline) { + _syncUpdateFlow.value = systemClock.epochMillis() + } override val membersStateFlow: StateFlow = _membersStateFlow.asStateFlow() @@ -147,6 +146,7 @@ class RustMatrixRoom( override fun destroy() { roomCoroutineScope.cancel() + timeline.close() innerRoom.destroy() roomListItem.destroy() specialModeEventTimelineItem?.destroy() @@ -195,7 +195,7 @@ class RustMatrixRoom( override suspend fun updateMembers(): Result = withContext(roomMembersDispatcher) { val currentState = _membersStateFlow.value - val currentMembers = currentState.roomMembers() + val currentMembers = currentState.roomMembers()?.toImmutableList() _membersStateFlow.value = MatrixRoomMembersState.Pending(prevRoomMembers = currentMembers) var rustMembers: List? = null try { @@ -210,7 +210,7 @@ class RustMatrixRoom( } } val mappedMembers = rustMembers.parallelMap(RoomMemberMapper::map) - _membersStateFlow.value = MatrixRoomMembersState.Ready(mappedMembers) + _membersStateFlow.value = MatrixRoomMembersState.Ready(mappedMembers.toImmutableList()) Result.success(Unit) } catch (exception: CancellationException) { _membersStateFlow.value = MatrixRoomMembersState.Error(prevRoomMembers = currentMembers, failure = exception) @@ -254,7 +254,7 @@ class RustMatrixRoom( override suspend fun sendMessage(body: String, htmlBody: String?, mentions: List): Result = withContext(roomDispatcher) { messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()).use { content -> runCatching { - innerRoom.send(content) + innerTimeline.send(content) } } } @@ -269,9 +269,9 @@ class RustMatrixRoom( withContext(roomDispatcher) { if (originalEventId != null) { runCatching { - val editedEvent = specialModeEventTimelineItem ?: innerRoom.getEventTimelineItemByEventId(originalEventId.value) + val editedEvent = specialModeEventTimelineItem ?: innerTimeline.getEventTimelineItemByEventId(originalEventId.value) editedEvent.use { - innerRoom.edit( + innerTimeline.edit( newContent = messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()), editItem = it, ) @@ -281,7 +281,7 @@ class RustMatrixRoom( } else { runCatching { transactionId?.let { cancelSend(it) } - innerRoom.send(messageEventContentFromParts(body, htmlBody)) + innerTimeline.send(messageEventContentFromParts(body, htmlBody)) } } } @@ -292,15 +292,15 @@ class RustMatrixRoom( runCatching { specialModeEventTimelineItem?.destroy() specialModeEventTimelineItem = null - specialModeEventTimelineItem = eventId?.let { innerRoom.getEventTimelineItemByEventId(it.value) } + specialModeEventTimelineItem = eventId?.let { innerTimeline.getEventTimelineItemByEventId(it.value) } } } override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List): Result = withContext(roomDispatcher) { runCatching { - val inReplyTo = specialModeEventTimelineItem ?: innerRoom.getEventTimelineItemByEventId(eventId.value) + val inReplyTo = specialModeEventTimelineItem ?: innerTimeline.getEventTimelineItemByEventId(eventId.value) inReplyTo.use { eventTimelineItem -> - innerRoom.sendReply(messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()), eventTimelineItem) + innerTimeline.sendReply(messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()), eventTimelineItem) } specialModeEventTimelineItem = null } @@ -360,39 +360,49 @@ class RustMatrixRoom( } } - override suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo, progressCallback: ProgressCallback?): Result { - return sendAttachment(listOf(file, thumbnailFile)) { - innerRoom.sendImage(file.path, thumbnailFile.path, imageInfo.map(), progressCallback?.toProgressWatcher()) + override suspend fun sendImage( + file: File, + thumbnailFile: File?, + imageInfo: ImageInfo, + progressCallback: ProgressCallback?, + ): Result { + return sendAttachment(listOfNotNull(file, thumbnailFile)) { + innerTimeline.sendImage(file.path, thumbnailFile?.path, imageInfo.map(), progressCallback?.toProgressWatcher()) } } - override suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo, progressCallback: ProgressCallback?): Result { - return sendAttachment(listOf(file, thumbnailFile)) { - innerRoom.sendVideo(file.path, thumbnailFile.path, videoInfo.map(), progressCallback?.toProgressWatcher()) + override suspend fun sendVideo( + file: File, + thumbnailFile: File?, + videoInfo: VideoInfo, + progressCallback: ProgressCallback?, + ): Result { + return sendAttachment(listOfNotNull(file, thumbnailFile)) { + innerTimeline.sendVideo(file.path, thumbnailFile?.path, videoInfo.map(), progressCallback?.toProgressWatcher()) } } override suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result { return sendAttachment(listOf(file)) { - innerRoom.sendAudio(file.path, audioInfo.map(), progressCallback?.toProgressWatcher()) + innerTimeline.sendAudio(file.path, audioInfo.map(), progressCallback?.toProgressWatcher()) } } override suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result { return sendAttachment(listOf(file)) { - innerRoom.sendFile(file.path, fileInfo.map(), progressCallback?.toProgressWatcher()) + innerTimeline.sendFile(file.path, fileInfo.map(), progressCallback?.toProgressWatcher()) } } override suspend fun toggleReaction(emoji: String, eventId: EventId): Result = withContext(roomDispatcher) { runCatching { - innerRoom.toggleReaction(key = emoji, eventId = eventId.value) + innerTimeline.toggleReaction(key = emoji, eventId = eventId.value) } } override suspend fun forwardEvent(eventId: EventId, roomIds: List): Result = withContext(roomDispatcher) { runCatching { - roomContentForwarder.forward(fromRoom = innerRoom, eventId = eventId, toRoomIds = roomIds) + roomContentForwarder.forward(fromTimeline = innerTimeline, eventId = eventId, toRoomIds = roomIds) }.onFailure { Timber.e(it) } @@ -400,13 +410,13 @@ class RustMatrixRoom( override suspend fun retrySendMessage(transactionId: TransactionId): Result = withContext(roomDispatcher) { runCatching { - innerRoom.retrySend(transactionId.value) + innerTimeline.retrySend(transactionId.value) } } override suspend fun cancelSend(transactionId: TransactionId): Result = withContext(roomDispatcher) { runCatching { - innerRoom.cancelSend(transactionId.value) + innerTimeline.cancelSend(transactionId.value) } } @@ -451,7 +461,7 @@ class RustMatrixRoom( assetType: AssetType?, ): Result = withContext(roomDispatcher) { runCatching { - innerRoom.sendLocation( + innerTimeline.sendLocation( body = body, geoUri = geoUri, description = description, @@ -468,7 +478,7 @@ class RustMatrixRoom( pollKind: PollKind, ): Result = withContext(roomDispatcher) { runCatching { - innerRoom.createPoll( + innerTimeline.createPoll( question = question, answers = answers, maxSelections = maxSelections.toUByte(), @@ -477,12 +487,36 @@ class RustMatrixRoom( } } + override suspend fun editPoll( + pollStartId: EventId, + question: String, + answers: List, + maxSelections: Int, + pollKind: PollKind, + ): Result = withContext(roomDispatcher) { + runCatching { + val pollStartEvent = + innerTimeline.getEventTimelineItemByEventId( + eventId = pollStartId.value + ) + pollStartEvent.use { + innerTimeline.editPoll( + question = question, + answers = answers, + maxSelections = maxSelections.toUByte(), + pollKind = pollKind.toInner(), + editItem = pollStartEvent, + ) + } + } + } + override suspend fun sendPollResponse( pollStartId: EventId, answers: List ): Result = withContext(roomDispatcher) { runCatching { - innerRoom.sendPollResponse( + innerTimeline.sendPollResponse( pollStartId = pollStartId.value, answers = answers, ) @@ -494,7 +528,7 @@ class RustMatrixRoom( text: String ): Result = withContext(roomDispatcher) { runCatching { - innerRoom.endPoll( + innerTimeline.endPoll( pollStartId = pollStartId.value, text = text, ) @@ -507,7 +541,7 @@ class RustMatrixRoom( waveform: List, progressCallback: ProgressCallback?, ): Result = sendAttachment(listOf(file)) { - innerRoom.sendVoiceMessage( + innerTimeline.sendVoiceMessage( url = file.path, audioInfo = audioInfo.map(), waveform = waveform.toMSC3246range(), @@ -536,12 +570,35 @@ class RustMatrixRoom( ) } - private suspend fun sendAttachment(files: List, handle: () -> SendAttachmentJoinHandle): Result { + override fun pollHistory() = AsyncMatrixTimeline( + coroutineScope = roomCoroutineScope, + dispatcher = roomDispatcher + ) { + val innerTimeline = innerRoom.pollHistory() + createMatrixTimeline(innerTimeline) + } + + private fun sendAttachment(files: List, handle: () -> SendAttachmentJoinHandle): Result { return runCatching { MediaUploadHandlerImpl(files, handle()) } } + private fun createMatrixTimeline( + timeline: InnerTimeline, + onNewSyncedEvent: () -> Unit = {}, + ): MatrixTimeline { + return RustMatrixTimeline( + isKeyBackupEnabled = isKeyBackupEnabled, + matrixRoom = this, + roomCoroutineScope = roomCoroutineScope, + dispatcher = roomDispatcher, + lastLoginTimestamp = sessionData.loginTimestamp, + onNewSyncedEvent = onNewSyncedEvent, + innerTimeline = timeline, + ) + } + private fun messageEventContentFromParts(body: String, htmlBody: String?): RoomMessageEventContentWithoutRelation = if (htmlBody != null) { messageEventContentFromHtml(body, htmlBody) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/StateEventType.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/StateEventType.kt index 2cd09e213c..fa2b731b61 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/StateEventType.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/StateEventType.kt @@ -23,6 +23,7 @@ fun StateEventType.map(): RustStateEventType = when (this) { StateEventType.POLICY_RULE_ROOM -> RustStateEventType.POLICY_RULE_ROOM StateEventType.POLICY_RULE_SERVER -> RustStateEventType.POLICY_RULE_SERVER StateEventType.POLICY_RULE_USER -> RustStateEventType.POLICY_RULE_USER + StateEventType.CALL_MEMBER -> RustStateEventType.CALL_MEMBER StateEventType.ROOM_ALIASES -> RustStateEventType.ROOM_ALIASES StateEventType.ROOM_AVATAR -> RustStateEventType.ROOM_AVATAR StateEventType.ROOM_CANONICAL_ALIAS -> RustStateEventType.ROOM_CANONICAL_ALIAS @@ -47,6 +48,7 @@ fun RustStateEventType.map(): StateEventType = when (this) { RustStateEventType.POLICY_RULE_ROOM -> StateEventType.POLICY_RULE_ROOM RustStateEventType.POLICY_RULE_SERVER -> StateEventType.POLICY_RULE_SERVER RustStateEventType.POLICY_RULE_USER -> StateEventType.POLICY_RULE_USER + RustStateEventType.CALL_MEMBER -> StateEventType.CALL_MEMBER RustStateEventType.ROOM_ALIASES -> StateEventType.ROOM_ALIASES RustStateEventType.ROOM_AVATAR -> StateEventType.ROOM_AVATAR RustStateEventType.ROOM_CANONICAL_ALIAS -> StateEventType.ROOM_CANONICAL_ALIAS diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/di/MatrixUIBindings.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListDynamicEvents.kt similarity index 62% rename from libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/di/MatrixUIBindings.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListDynamicEvents.kt index 919971b307..97d21f83f2 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/di/MatrixUIBindings.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListDynamicEvents.kt @@ -14,13 +14,12 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.ui.di +package io.element.android.libraries.matrix.impl.roomlist -import com.squareup.anvil.annotations.ContributesTo -import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.ui.media.LoggedInImageLoaderFactory +import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind -@ContributesTo(SessionScope::class) -interface MatrixUIBindings { - fun loggedInImageLoaderFactory(): LoggedInImageLoaderFactory +internal sealed interface RoomListDynamicEvents { + data object Reset : RoomListDynamicEvents + data object LoadMore : RoomListDynamicEvents + data class SetFilter(val filter: RoomListEntriesDynamicFilterKind) : RoomListDynamicEvents } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt index b95eb95333..79ea6b17e0 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt @@ -17,15 +17,20 @@ package io.element.android.libraries.matrix.impl.roomlist import io.element.android.libraries.core.data.tryOrNull +import io.element.android.libraries.matrix.impl.util.cancelAndDestroy import io.element.android.libraries.matrix.impl.util.mxCallbackFlow import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.trySendBlocking import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.buffer +import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind import org.matrix.rustcomponents.sdk.RoomListEntriesListener import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate -import org.matrix.rustcomponents.sdk.RoomListEntry import org.matrix.rustcomponents.sdk.RoomListInterface import org.matrix.rustcomponents.sdk.RoomListItem import org.matrix.rustcomponents.sdk.RoomListLoadingState @@ -58,25 +63,42 @@ fun RoomListInterface.loadingStateFlow(): Flow = Timber.d(it, "loadingStateFlow() failed") }.buffer(Channel.UNLIMITED) -fun RoomListInterface.entriesFlow(onInitialList: suspend (List) -> Unit): Flow> = - mxCallbackFlow { +internal fun RoomListInterface.entriesFlow( + pageSize: Int, + roomListDynamicEvents: Flow, + initialFilterKind: RoomListEntriesDynamicFilterKind +): Flow> = + callbackFlow { val listener = object : RoomListEntriesListener { override fun onUpdate(roomEntriesUpdate: List) { trySendBlocking(roomEntriesUpdate) } } - val result = entries(listener) - try { - onInitialList(result.entries) - } catch (exception: Exception) { - Timber.d("entriesFlow() onInitialList failed.") + val result = entriesWithDynamicAdapters(pageSize.toUInt(), listener) + val controller = result.controller + controller.setFilter(initialFilterKind) + roomListDynamicEvents.onEach { controllerEvents -> + when (controllerEvents) { + is RoomListDynamicEvents.SetFilter -> { + controller.setFilter(controllerEvents.filter) + } + is RoomListDynamicEvents.LoadMore -> { + controller.addOnePage() + } + is RoomListDynamicEvents.Reset -> { + controller.resetToOnePage() + } + } + }.launchIn(this) + awaitClose { + result.entriesStream.cancelAndDestroy() + result.destroy() } - result.entriesStream }.catch { Timber.d(it, "entriesFlow() failed") }.buffer(Channel.UNLIMITED) -fun RoomListServiceInterface.stateFlow(): Flow = +internal fun RoomListServiceInterface.stateFlow(): Flow = mxCallbackFlow { val listener = object : RoomListServiceStateListener { override fun onUpdate(state: RoomListServiceState) { @@ -88,7 +110,7 @@ fun RoomListServiceInterface.stateFlow(): Flow = } }.buffer(Channel.UNLIMITED) -fun RoomListServiceInterface.syncIndicator(): Flow = +internal fun RoomListServiceInterface.syncIndicator(): Flow = mxCallbackFlow { val listener = object : RoomListServiceSyncIndicatorListener { override fun onUpdate(syncIndicator: RoomListServiceSyncIndicator) { @@ -104,7 +126,7 @@ fun RoomListServiceInterface.syncIndicator(): Flow } }.buffer(Channel.UNLIMITED) -fun RoomListServiceInterface.roomOrNull(roomId: String): RoomListItem? { +internal fun RoomListServiceInterface.roomOrNull(roomId: String): RoomListItem? { return try { room(roomId) } catch (exception: Exception) { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt new file mode 100644 index 0000000000..17bff4f5b7 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrix.impl.roomlist + +import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList +import io.element.android.libraries.matrix.api.roomlist.RoomList +import io.element.android.libraries.matrix.api.roomlist.RoomSummary +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.getAndUpdate +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind +import org.matrix.rustcomponents.sdk.RoomListLoadingState +import org.matrix.rustcomponents.sdk.RoomList as InnerRoomList +import org.matrix.rustcomponents.sdk.RoomListService as InnerRoomListService + +internal class RoomListFactory( + private val innerRoomListService: InnerRoomListService, + private val coroutineScope: CoroutineScope, + private val dispatcher: CoroutineDispatcher, + private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(), +) { + + /** + * Creates a room list that can be used to load more rooms and filter them dynamically. + */ + fun createRoomList( + pageSize: Int, + initialFilter: DynamicRoomList.Filter = DynamicRoomList.Filter.All, + innerProvider: suspend () -> InnerRoomList + ): DynamicRoomList { + val loadingStateFlow: MutableStateFlow = MutableStateFlow(RoomList.LoadingState.NotLoaded) + val summariesFlow = MutableStateFlow>(emptyList()) + val processor = RoomSummaryListProcessor(summariesFlow, innerRoomListService, dispatcher, roomSummaryDetailsFactory) + // Makes sure we don't miss any events + val dynamicEvents = MutableSharedFlow(replay = 100) + val currentFilter = MutableStateFlow(initialFilter) + val loadedPages = MutableStateFlow(1) + var innerRoomList: InnerRoomList? = null + coroutineScope.launch(dispatcher) { + innerRoomList = innerProvider() + innerRoomList?.let { innerRoomList -> + innerRoomList.entriesFlow( + pageSize = pageSize, + initialFilterKind = initialFilter.toRustFilter(), + roomListDynamicEvents = dynamicEvents + ).onEach { update -> + processor.postUpdate(update) + }.launchIn(this) + + innerRoomList.loadingStateFlow() + .map { it.toLoadingState() } + .onEach { + loadingStateFlow.value = it + } + .launchIn(this) + } + }.invokeOnCompletion { + innerRoomList?.destroy() + } + return RustDynamicRoomList( + summaries = summariesFlow, + loadingState = loadingStateFlow, + currentFilter = currentFilter, + loadedPages = loadedPages, + dynamicEvents = dynamicEvents, + processor = processor, + pageSize = pageSize, + ) + } +} + +private class RustDynamicRoomList( + override val summaries: MutableStateFlow>, + override val loadingState: MutableStateFlow, + override val currentFilter: MutableStateFlow, + override val loadedPages: MutableStateFlow, + private val dynamicEvents: MutableSharedFlow, + private val processor: RoomSummaryListProcessor, + override val pageSize: Int, +) : DynamicRoomList { + + override suspend fun rebuildSummaries() { + processor.rebuildRoomSummaries() + } + + override suspend fun updateFilter(filter: DynamicRoomList.Filter) { + currentFilter.emit(filter) + val filterEvent = RoomListDynamicEvents.SetFilter(filter.toRustFilter()) + dynamicEvents.emit(filterEvent) + } + + override suspend fun loadMore() { + dynamicEvents.emit(RoomListDynamicEvents.LoadMore) + loadedPages.getAndUpdate { it + 1 } + } + + override suspend fun reset() { + dynamicEvents.emit(RoomListDynamicEvents.Reset) + loadedPages.emit(1) + } +} + +private fun RoomListLoadingState.toLoadingState(): RoomList.LoadingState { + return when (this) { + is RoomListLoadingState.Loaded -> RoomList.LoadingState.Loaded(maximumNumberOfRooms?.toInt() ?: 0) + RoomListLoadingState.NotLoaded -> RoomList.LoadingState.NotLoaded + } +} + +private fun DynamicRoomList.Filter.toRustFilter(): RoomListEntriesDynamicFilterKind { + return when (this) { + DynamicRoomList.Filter.All -> RoomListEntriesDynamicFilterKind.All + is DynamicRoomList.Filter.NormalizedMatchRoomName -> RoomListEntriesDynamicFilterKind.NormalizedMatchRoomName(this.pattern) + DynamicRoomList.Filter.None -> RoomListEntriesDynamicFilterKind.None + DynamicRoomList.Filter.AllNonLeft -> RoomListEntriesDynamicFilterKind.AllNonLeft + } +} + diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt index d0e3d1c8cf..f0dd3a4176 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt @@ -16,9 +16,7 @@ package io.element.android.libraries.matrix.impl.roomlist -import io.element.android.libraries.core.coroutine.parallelMap import io.element.android.libraries.matrix.api.roomlist.RoomSummary -import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.sync.Mutex @@ -39,21 +37,9 @@ class RoomSummaryListProcessor( ) { private val roomSummariesByIdentifier = HashMap() - private val initLatch = CompletableDeferred() private val mutex = Mutex() - suspend fun postEntries(entries: List) { - updateRoomSummaries { - Timber.v("Update rooms from postEntries (with ${entries.size} items) on ${Thread.currentThread()}") - val roomSummaries = entries.parallelMap(::buildSummaryForRoomListEntry) - addAll(roomSummaries) - } - initLatch.complete(Unit) - } - suspend fun postUpdate(updates: List) { - // Makes sure to process first entries before update. - initLatch.await() updateRoomSummaries { Timber.v("Update rooms from postUpdates (with ${updates.size} items) on ${Thread.currentThread()}") updates.forEach { update -> @@ -65,7 +51,7 @@ class RoomSummaryListProcessor( suspend fun rebuildRoomSummaries() { updateRoomSummaries { forEachIndexed { i, summary -> - this[i] = when(summary) { + this[i] = when (summary) { is RoomSummary.Empty -> summary is RoomSummary.Filled -> buildAndCacheRoomSummaryForIdentifier(summary.identifier()) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt index 47b4792ca3..3dce8a0fc4 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt @@ -16,81 +16,49 @@ package io.element.android.libraries.matrix.impl.roomlist +import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList import io.element.android.libraries.matrix.api.roomlist.RoomList import io.element.android.libraries.matrix.api.roomlist.RoomListService -import io.element.android.libraries.matrix.api.roomlist.RoomSummary -import kotlinx.coroutines.CoroutineDispatcher +import io.element.android.libraries.matrix.api.roomlist.loadAllIncrementally import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate import org.matrix.rustcomponents.sdk.RoomListException import org.matrix.rustcomponents.sdk.RoomListInput -import org.matrix.rustcomponents.sdk.RoomListLoadingState import org.matrix.rustcomponents.sdk.RoomListRange import org.matrix.rustcomponents.sdk.RoomListServiceState import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator import timber.log.Timber import org.matrix.rustcomponents.sdk.RoomListService as InnerRustRoomListService -class RustRoomListService( +private const val DEFAULT_PAGE_SIZE = 20 + +internal class RustRoomListService( private val innerRoomListService: InnerRustRoomListService, private val sessionCoroutineScope: CoroutineScope, - private val dispatcher: CoroutineDispatcher, - roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(), + roomListFactory: RoomListFactory, ) : RoomListService { - private val allRooms = MutableStateFlow>(emptyList()) - private val inviteRooms = MutableStateFlow>(emptyList()) + override val allRooms: DynamicRoomList = roomListFactory.createRoomList( + pageSize = DEFAULT_PAGE_SIZE, + initialFilter = DynamicRoomList.Filter.AllNonLeft, + ) { + innerRoomListService.allRooms() + } - private val allRoomsLoadingState: MutableStateFlow = MutableStateFlow(RoomList.LoadingState.NotLoaded) - private val allRoomsListProcessor = RoomSummaryListProcessor(allRooms, innerRoomListService, dispatcher, roomSummaryDetailsFactory) - private val invitesLoadingState: MutableStateFlow = MutableStateFlow(RoomList.LoadingState.NotLoaded) - private val inviteRoomsListProcessor = RoomSummaryListProcessor(inviteRooms, innerRoomListService, dispatcher, roomSummaryDetailsFactory) + override val invites: RoomList = roomListFactory.createRoomList( + pageSize = Int.MAX_VALUE, + ) { + innerRoomListService.invites() + } init { - sessionCoroutineScope.launch(dispatcher) { - val allRooms = innerRoomListService.allRooms() - allRooms - .observeEntriesWithProcessor(allRoomsListProcessor) - .launchIn(this) - allRooms - .observeLoadingState(allRoomsLoadingState) - .launchIn(this) - - - launch { - // Wait until running, as invites is only available after that - innerRoomListService.stateFlow().first { - it == RoomListServiceState.RUNNING - } - val invites = innerRoomListService.invites() - invites - .observeEntriesWithProcessor(inviteRoomsListProcessor) - .launchIn(this) - invites - .observeLoadingState(invitesLoadingState) - .launchIn(this) - - } - } - } - - override fun allRooms(): RoomList { - return RustRoomList(allRooms, allRoomsLoadingState) - } - - override fun invites(): RoomList { - return RustRoomList(inviteRooms, invitesLoadingState) + allRooms.loadAllIncrementally(sessionCoroutineScope) } override fun updateAllRoomsVisibleRange(range: IntRange) { @@ -107,12 +75,6 @@ class RustRoomListService( } } - override fun rebuildRoomSummaries() { - sessionCoroutineScope.launch { - allRoomsListProcessor.rebuildRoomSummaries() - } - } - override val syncIndicator: StateFlow = innerRoomListService.syncIndicator() .map { it.toSyncIndicator() } @@ -132,13 +94,6 @@ class RustRoomListService( .stateIn(sessionCoroutineScope, SharingStarted.Eagerly, RoomListService.State.Idle) } -private fun RoomListLoadingState.toLoadingState(): RoomList.LoadingState { - return when (this) { - is RoomListLoadingState.Loaded -> RoomList.LoadingState.Loaded(maximumNumberOfRooms?.toInt() ?: 0) - RoomListLoadingState.NotLoaded -> RoomList.LoadingState.NotLoaded - } -} - private fun RoomListServiceState.toRoomListState(): RoomListService.State { return when (this) { RoomListServiceState.INITIAL, @@ -156,20 +111,3 @@ private fun RoomListServiceSyncIndicator.toSyncIndicator(): RoomListService.Sync RoomListServiceSyncIndicator.HIDE -> RoomListService.SyncIndicator.Hide } } - -private fun org.matrix.rustcomponents.sdk.RoomList.observeEntriesWithProcessor(processor: RoomSummaryListProcessor): Flow> { - return entriesFlow { roomListEntries -> - processor.postEntries(roomListEntries) - }.onEach { update -> - processor.postUpdate(update) - } -} - -private fun org.matrix.rustcomponents.sdk.RoomList.observeLoadingState(stateFlow: MutableStateFlow): Flow { - return loadingStateFlow() - .map { it.toLoadingState() } - .onEach { - stateFlow.value = it - } -} - diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/AsyncMatrixTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/AsyncMatrixTimeline.kt new file mode 100644 index 0000000000..9be6d3e3bd --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/AsyncMatrixTimeline.kt @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrix.impl.timeline + +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.timeline.MatrixTimeline +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.ReceiptType +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.NonCancellable +import kotlinx.coroutines.async +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import timber.log.Timber + +/** + * This class is a wrapper around a [MatrixTimeline] that will be created asynchronously. + */ +class AsyncMatrixTimeline( + coroutineScope: CoroutineScope, + dispatcher: CoroutineDispatcher, + private val timelineProvider: suspend () -> MatrixTimeline +) : MatrixTimeline { + + private val _timelineItems: MutableStateFlow> = + MutableStateFlow(emptyList()) + + private val _paginationState = MutableStateFlow( + MatrixTimeline.PaginationState.Initial + ) + private val timeline = coroutineScope.async(context = dispatcher, start = CoroutineStart.LAZY) { + timelineProvider() + } + private val closeSignal = CompletableDeferred() + + init { + coroutineScope.launch { + val delegateTimeline = timeline.await() + delegateTimeline.timelineItems + .onEach { _timelineItems.value = it } + .launchIn(this) + delegateTimeline.paginationState + .onEach { _paginationState.value = it } + .launchIn(this) + + launch { + withContext(NonCancellable) { + closeSignal.await() + Timber.d("Close delegate") + delegateTimeline.close() + } + } + } + } + + override val paginationState: StateFlow = _paginationState + override val timelineItems: Flow> = _timelineItems + + override suspend fun paginateBackwards(requestSize: Int): Result { + return timeline.await().paginateBackwards(requestSize) + } + + override suspend fun paginateBackwards(requestSize: Int, untilNumberOfItems: Int): Result { + return timeline.await().paginateBackwards(requestSize, untilNumberOfItems) + } + + override suspend fun fetchDetailsForEvent(eventId: EventId): Result { + return timeline.await().fetchDetailsForEvent(eventId) + } + + override suspend fun sendReadReceipt(eventId: EventId, receiptType: ReceiptType): Result { + return timeline.await().sendReadReceipt(eventId, receiptType) + } + + override fun close() { + closeSignal.complete(Unit) + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItemMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItemMapper.kt index 2e86aa0706..beca5f17fb 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItemMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItemMapper.kt @@ -32,7 +32,7 @@ class MatrixTimelineItemMapper( ) { fun map(timelineItem: TimelineItem): MatrixTimelineItem = timelineItem.use { - val uniqueId = timelineItem.uniqueId().toLong() + val uniqueId = timelineItem.uniqueId().toString() val asEvent = it.asEvent() if (asEvent != null) { val eventTimelineItem = eventTimelineItemMapper.map(asEvent) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/ReceiptTypeMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/ReceiptTypeMapper.kt new file mode 100644 index 0000000000..02722e43dc --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/ReceiptTypeMapper.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrix.impl.timeline + +import io.element.android.libraries.matrix.api.timeline.ReceiptType +import org.matrix.rustcomponents.sdk.ReceiptType as RustReceiptType + +internal fun ReceiptType.toRustReceiptType(): RustReceiptType = when (this) { + ReceiptType.READ -> RustReceiptType.READ + ReceiptType.READ_PRIVATE -> RustReceiptType.READ_PRIVATE + ReceiptType.FULLY_READ -> RustReceiptType.FULLY_READ +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt index e87ae74f30..8eb2f999d1 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt @@ -29,29 +29,28 @@ import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.catch import org.matrix.rustcomponents.sdk.BackPaginationStatus import org.matrix.rustcomponents.sdk.BackPaginationStatusListener -import org.matrix.rustcomponents.sdk.Room +import org.matrix.rustcomponents.sdk.Timeline import org.matrix.rustcomponents.sdk.TimelineDiff import org.matrix.rustcomponents.sdk.TimelineItem import org.matrix.rustcomponents.sdk.TimelineListener import timber.log.Timber -internal fun Room.timelineDiffFlow(onInitialList: suspend (List) -> Unit): Flow> = +internal fun Timeline.timelineDiffFlow(onInitialList: suspend (List) -> Unit): Flow> = callbackFlow { val listener = object : TimelineListener { override fun onUpdate(diff: List) { trySendBlocking(diff) } } - val roomId = id() - Timber.d("Open timelineDiffFlow for room $roomId") - val result = addTimelineListener(listener) + Timber.d("Open timelineDiffFlow for TimelineInterface ${this@timelineDiffFlow}") + val result = addListener(listener) try { onInitialList(result.items) } catch (exception: Exception) { - Timber.d(exception, "Catch failure in timelineDiffFlow of room $roomId") + Timber.d(exception, "Catch failure in timelineDiffFlow of TimelineInterface ${this@timelineDiffFlow}") } awaitClose { - Timber.d("Close timelineDiffFlow for room $roomId") + Timber.d("Close timelineDiffFlow for TimelineInterface ${this@timelineDiffFlow}") result.itemsStream.cancelAndDestroy() result.items.destroyAll() } @@ -59,7 +58,7 @@ internal fun Room.timelineDiffFlow(onInitialList: suspend (List) - Timber.d(it, "timelineDiffFlow() failed") }.buffer(Channel.UNLIMITED) -internal fun Room.backPaginationStatusFlow(): Flow = +internal fun Timeline.backPaginationStatusFlow(): Flow = mxCallbackFlow { val listener = object : BackPaginationStatusListener { override fun onUpdate(status: BackPaginationStatus) { @@ -71,8 +70,8 @@ internal fun Room.backPaginationStatusFlow(): Flow = } }.buffer(Channel.UNLIMITED) -internal suspend fun Room.runWithTimelineListenerRegistered(action: suspend () -> Unit) { - val result = addTimelineListener(NoOpTimelineListener) +internal suspend fun Timeline.runWithTimelineListenerRegistered(action: suspend () -> Unit) { + val result = addListener(NoOpTimelineListener) try { action() } finally { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt index 85e6905f0a..547ccf1377 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt @@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.TimelineException import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem import io.element.android.libraries.matrix.impl.timeline.item.event.EventMessageMapper @@ -37,7 +38,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.getAndUpdate +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onEach @@ -46,7 +47,7 @@ import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.BackPaginationStatus import org.matrix.rustcomponents.sdk.EventItemOrigin import org.matrix.rustcomponents.sdk.PaginationOptions -import org.matrix.rustcomponents.sdk.Room +import org.matrix.rustcomponents.sdk.Timeline import org.matrix.rustcomponents.sdk.TimelineDiff import org.matrix.rustcomponents.sdk.TimelineItem import timber.log.Timber @@ -59,9 +60,9 @@ class RustMatrixTimeline( roomCoroutineScope: CoroutineScope, isKeyBackupEnabled: Boolean, private val matrixRoom: MatrixRoom, - private val innerRoom: Room, + private val innerTimeline: Timeline, private val dispatcher: CoroutineDispatcher, - private val lastLoginTimestamp: Date?, + lastLoginTimestamp: Date?, private val onNewSyncedEvent: () -> Unit, ) : MatrixTimeline { @@ -72,18 +73,13 @@ class RustMatrixTimeline( MutableStateFlow(emptyList()) private val _paginationState = MutableStateFlow( - MatrixTimeline.PaginationState( - hasMoreToLoadBackwards = true, - isBackPaginating = false, - beginningOfRoomReached = false, - ) + MatrixTimeline.PaginationState.Initial ) private val encryptedHistoryPostProcessor = TimelineEncryptedHistoryPostProcessor( lastLoginTimestamp = lastLoginTimestamp, isRoomEncrypted = matrixRoom.isEncrypted, isKeyBackupEnabled = isKeyBackupEnabled, - paginationStateFlow = _paginationState, dispatcher = dispatcher, ) @@ -105,11 +101,16 @@ class RustMatrixTimeline( override val paginationState: StateFlow = _paginationState.asStateFlow() + @OptIn(ExperimentalCoroutinesApi::class) + override val timelineItems: Flow> = _timelineItems.mapLatest { items -> + encryptedHistoryPostProcessor.process(items) + } + init { Timber.d("Initialize timeline for room ${matrixRoom.roomId}") roomCoroutineScope.launch(dispatcher) { - innerRoom.timelineDiffFlow { initialList -> + innerTimeline.timelineDiffFlow { initialList -> postItems(initialList) }.onEach { diffs -> if (diffs.any { diff -> diff.eventOrigin() == EventItemOrigin.SYNC }) { @@ -118,9 +119,9 @@ class RustMatrixTimeline( postDiffs(diffs) }.launchIn(this) - innerRoom.backPaginationStatusFlow() + paginationStateFlow() .onEach { - postPaginationStatus(it) + _paginationState.value = it } .launchIn(this) @@ -128,14 +129,51 @@ class RustMatrixTimeline( } } - private suspend fun fetchMembers() = withContext(dispatcher) { - initLatch.await() - innerRoom.fetchMembers() + private fun paginationStateFlow(): Flow { + return combine( + innerTimeline.backPaginationStatusFlow(), + timelineItems, + ) { paginationStatus, filteredItems -> + if (filteredItems.hasEncryptionHistoryBanner()) { + return@combine MatrixTimeline.PaginationState( + isBackPaginating = false, + hasMoreToLoadBackwards = false, + beginningOfRoomReached = false, + ) + } + when (paginationStatus) { + BackPaginationStatus.IDLE -> { + MatrixTimeline.PaginationState( + isBackPaginating = false, + hasMoreToLoadBackwards = true, + beginningOfRoomReached = false, + ) + } + BackPaginationStatus.PAGINATING -> { + MatrixTimeline.PaginationState( + isBackPaginating = true, + hasMoreToLoadBackwards = true, + beginningOfRoomReached = false, + ) + } + BackPaginationStatus.TIMELINE_START_REACHED -> { + MatrixTimeline.PaginationState( + isBackPaginating = false, + hasMoreToLoadBackwards = false, + beginningOfRoomReached = true, + ) + } + } + } } - @OptIn(ExperimentalCoroutinesApi::class) - override val timelineItems: Flow> = _timelineItems.mapLatest { items -> - encryptedHistoryPostProcessor.process(items) + private suspend fun fetchMembers() = withContext(dispatcher) { + initLatch.await() + try { + innerTimeline.fetchMembers() + } catch (exception: Exception) { + Timber.e(exception, "Error fetching members for room ${matrixRoom.roomId}") + } } private suspend fun postItems(items: List) = coroutineScope { @@ -153,55 +191,35 @@ class RustMatrixTimeline( timelineDiffProcessor.postDiffs(diffs) } - private fun postPaginationStatus(status: BackPaginationStatus) { - _paginationState.getAndUpdate { currentPaginationState -> - if (hasEncryptionHistoryBanner()) { - return@getAndUpdate currentPaginationState.copy( - isBackPaginating = false, - hasMoreToLoadBackwards = false, - beginningOfRoomReached = false, - ) - } - when (status) { - BackPaginationStatus.IDLE -> { - currentPaginationState.copy( - isBackPaginating = false, - hasMoreToLoadBackwards = true - ) - } - BackPaginationStatus.PAGINATING -> { - currentPaginationState.copy( - isBackPaginating = true, - hasMoreToLoadBackwards = true - ) - } - BackPaginationStatus.TIMELINE_START_REACHED -> { - currentPaginationState.copy( - isBackPaginating = false, - hasMoreToLoadBackwards = false, - beginningOfRoomReached = true, - ) - } - } - } - } - override suspend fun fetchDetailsForEvent(eventId: EventId): Result = withContext(dispatcher) { runCatching { - innerRoom.fetchDetailsForEvent(eventId.value) + innerTimeline.fetchDetailsForEvent(eventId.value) } } - override suspend fun paginateBackwards(requestSize: Int, untilNumberOfItems: Int): Result = withContext(dispatcher) { + override suspend fun paginateBackwards(requestSize: Int): Result { + val paginationOptions = PaginationOptions.SimpleRequest( + eventLimit = requestSize.toUShort(), + waitForToken = true, + ) + return paginateBackwards(paginationOptions) + } + + override suspend fun paginateBackwards(requestSize: Int, untilNumberOfItems: Int): Result { + val paginationOptions = PaginationOptions.UntilNumItems( + eventLimit = requestSize.toUShort(), + items = untilNumberOfItems.toUShort(), + waitForToken = true, + ) + return paginateBackwards(paginationOptions) + } + + private suspend fun paginateBackwards(paginationOptions: PaginationOptions): Result = withContext(dispatcher) { + initLatch.await() runCatching { if (!canBackPaginate()) throw TimelineException.CannotPaginate Timber.v("Start back paginating for room ${matrixRoom.roomId} ") - val paginationOptions = PaginationOptions.UntilNumItems( - eventLimit = requestSize.toUShort(), - items = untilNumberOfItems.toUShort(), - waitForToken = true, - ) - innerRoom.paginateBackwards(paginationOptions) + innerTimeline.paginateBackwards(paginationOptions) }.onFailure { error -> if (error is TimelineException.CannotPaginate) { Timber.d("Can't paginate backwards on room ${matrixRoom.roomId}, we're already at the start") @@ -217,18 +235,28 @@ class RustMatrixTimeline( return isInit.get() && paginationState.value.canBackPaginate } - override suspend fun sendReadReceipt(eventId: EventId) = withContext(dispatcher) { + override suspend fun sendReadReceipt( + eventId: EventId, + receiptType: ReceiptType, + ) = withContext(dispatcher) { runCatching { - innerRoom.sendReadReceipt(eventId = eventId.value) + innerTimeline.sendReadReceipt( + receiptType = receiptType.toRustReceiptType(), + eventId = eventId.value, + ) } } + override fun close() { + innerTimeline.close() + } + fun getItemById(eventId: EventId): MatrixTimelineItem.Event? { return _timelineItems.value.firstOrNull { (it as? MatrixTimelineItem.Event)?.eventId == eventId } as? MatrixTimelineItem.Event } - private fun hasEncryptionHistoryBanner(): Boolean { - val firstItem = _timelineItems.value.firstOrNull() + private fun List.hasEncryptionHistoryBanner(): Boolean { + val firstItem = firstOrNull() return firstItem is MatrixTimelineItem.Virtual && firstItem.virtual is VirtualTimelineItem.EncryptedHistoryBanner } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt index 7f88cc0569..4f19a5c40f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt @@ -30,7 +30,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType import io.element.android.libraries.matrix.api.timeline.item.event.OtherMessageType import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType -import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType import io.element.android.libraries.matrix.impl.media.map @@ -76,7 +75,7 @@ class EventMessageMapper { ) } - fun mapMessageType(type: RustMessageType?) = when (type) { + fun mapMessageType(type: RustMessageType) = when (type) { is RustMessageType.Audio -> { when (type.content.voice) { null -> { @@ -120,7 +119,6 @@ class EventMessageMapper { is MessageType.Other -> { OtherMessageType(type.msgtype, type.body) } - null -> UnknownMessageType } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt index d761e91d6c..4e965fc4ef 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt @@ -27,6 +27,9 @@ import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimeli import io.element.android.libraries.matrix.api.timeline.item.event.ReactionSender import io.element.android.libraries.matrix.api.timeline.item.event.Receipt import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList import org.matrix.rustcomponents.sdk.Reaction import org.matrix.rustcomponents.sdk.EventItemOrigin as RustEventItemOrigin import org.matrix.rustcomponents.sdk.EventSendState as RustEventSendState @@ -81,7 +84,7 @@ fun RustEventSendState?.map(): LocalEventSendState? { } } -private fun List?.map(): List { +private fun List?.map(): ImmutableList { return this?.map { EventReaction( key = it.key, @@ -90,18 +93,20 @@ private fun List?.map(): List { senderId = UserId(sender.senderId), timestamp = sender.timestamp.toLong() ) - } + }.toImmutableList() ) - } ?: emptyList() + }?.toImmutableList() ?: persistentListOf() } -private fun Map.map(): List { +private fun Map.map(): ImmutableList { return map { - Receipt( - userId = UserId(it.key), - timestamp = it.value.timestamp?.toLong() ?: 0 - ) - }.sortedByDescending { it.timestamp } + Receipt( + userId = UserId(it.key), + timestamp = it.value.timestamp?.toLong() ?: 0 + ) + } + .sortedByDescending { it.timestamp } + .toImmutableList() } private fun RustEventTimelineItemDebugInfo.map(): TimelineItemDebugInfo { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt index 01dd32c048..ee28b18d08 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt @@ -32,6 +32,8 @@ import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecry import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent import io.element.android.libraries.matrix.impl.media.map import io.element.android.libraries.matrix.impl.poll.map +import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.toImmutableMap import org.matrix.rustcomponents.sdk.TimelineItemContent import org.matrix.rustcomponents.sdk.TimelineItemContentKind import org.matrix.rustcomponents.sdk.use @@ -106,11 +108,12 @@ class TimelineEventContentMapper(private val eventMessageMapper: EventMessageMap question = kind.question, kind = kind.kind.map(), maxSelections = kind.maxSelections, - answers = kind.answers.map { answer -> answer.map() }, + answers = kind.answers.map { answer -> answer.map() }.toImmutableList(), votes = kind.votes.mapValues { vote -> - vote.value.map { userId -> UserId(userId) } - }, + vote.value.map { userId -> UserId(userId) }.toImmutableList() + }.toImmutableMap(), endTime = kind.endTime, + isEdited = kind.hasBeenEdited, ) } is TimelineItemContentKind.UnableToDecrypt -> { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt index 25241ff3e1..9c0fbfd115 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt @@ -16,12 +16,9 @@ package io.element.android.libraries.matrix.impl.timeline.postprocessor -import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.getAndUpdate import kotlinx.coroutines.withContext import timber.log.Timber import java.util.Date @@ -31,26 +28,12 @@ class TimelineEncryptedHistoryPostProcessor( private val lastLoginTimestamp: Date?, private val isRoomEncrypted: Boolean, private val isKeyBackupEnabled: Boolean, - private val paginationStateFlow: MutableStateFlow, ) { suspend fun process(items: List): List = withContext(dispatcher) { Timber.d("Process on Thread=${Thread.currentThread()}") if (!isRoomEncrypted || isKeyBackupEnabled || lastLoginTimestamp == null) return@withContext items - - val filteredItems = replaceWithEncryptionHistoryBannerIfNeeded(items) - // Disable back pagination - val wasFiltered = filteredItems !== items - if (wasFiltered) { - paginationStateFlow.getAndUpdate { - it.copy( - isBackPaginating = false, - hasMoreToLoadBackwards = false, - beginningOfRoomReached = false, - ) - } - } - filteredItems + replaceWithEncryptionHistoryBannerIfNeeded(items) } private fun replaceWithEncryptionHistoryBannerIfNeeded(list: List): List { @@ -62,7 +45,7 @@ class TimelineEncryptedHistoryPostProcessor( } return if (lastEncryptedHistoryBannerIndex >= 0) { val sublist = list.drop(lastEncryptedHistoryBannerIndex + 1).toMutableList() - sublist.add(0, MatrixTimelineItem.Virtual(0L, VirtualTimelineItem.EncryptedHistoryBanner)) + sublist.add(0, MatrixTimelineItem.Virtual(VirtualTimelineItem.EncryptedHistoryBanner.toString(), VirtualTimelineItem.EncryptedHistoryBanner)) sublist } else { list diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserSearchResultMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserSearchResultMapper.kt index 1ec0b512ec..b95cbaeed1 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserSearchResultMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserSearchResultMapper.kt @@ -17,13 +17,14 @@ package io.element.android.libraries.matrix.impl.usersearch import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults +import kotlinx.collections.immutable.toImmutableList import org.matrix.rustcomponents.sdk.SearchUsersResults object UserSearchResultMapper { fun map(result: SearchUsersResults): MatrixSearchUserResults { return MatrixSearchUserResults( - results = result.results.map(UserProfileMapper::map), + results = result.results.map(UserProfileMapper::map).toImmutableList(), limited = result.limited, ) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/RustSessionVerificationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/RustSessionVerificationService.kt index a4dfadfa5b..0d705d6834 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/RustSessionVerificationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/RustSessionVerificationService.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl.verification import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.matrix.api.sync.SyncState +import io.element.android.libraries.matrix.api.verification.SessionVerificationData import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.matrix.api.verification.VerificationEmoji @@ -32,7 +33,8 @@ import kotlinx.coroutines.launch import org.matrix.rustcomponents.sdk.SessionVerificationController import org.matrix.rustcomponents.sdk.SessionVerificationControllerDelegate import org.matrix.rustcomponents.sdk.SessionVerificationControllerInterface -import org.matrix.rustcomponents.sdk.SessionVerificationEmoji +import org.matrix.rustcomponents.sdk.use +import org.matrix.rustcomponents.sdk.SessionVerificationData as RustSessionVerificationData class RustSessionVerificationService( private val syncService: RustSyncService, @@ -104,11 +106,8 @@ class RustSessionVerificationService( updateVerificationStatus(isVerified = true) } - override fun didReceiveVerificationData(data: List) { - val emojis = data.map { emoji -> - emoji.use { VerificationEmoji(it.symbol(), it.description()) } - } - _verificationFlowState.value = VerificationFlowState.ReceivedVerificationData(emojis) + override fun didReceiveVerificationData(data: RustSessionVerificationData) { + _verificationFlowState.value = VerificationFlowState.ReceivedVerificationData(data.map()) } // When the actual SAS verification starts @@ -140,3 +139,28 @@ class RustSessionVerificationService( _sessionVerifiedStatus.value = newValue } } + +private fun RustSessionVerificationData.map(): SessionVerificationData { + return use { sessionVerificationData -> + when (sessionVerificationData) { + is RustSessionVerificationData.Emojis -> { + SessionVerificationData.Emojis( + emojis = sessionVerificationData.emojis.mapIndexed { index, emoji -> + emoji.use { sessionVerificationEmoji -> + VerificationEmoji( + number = sessionVerificationData.indices[index].toInt(), + emoji = sessionVerificationEmoji.symbol(), + description = sessionVerificationEmoji.description(), + ) + } + }, + ) + } + is RustSessionVerificationData.Decimals -> { + SessionVerificationData.Decimals( + decimals = sessionVerificationData.values.map { it.toInt() }, + ) + } + } + } +} diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTests.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTests.kt index 5a376ea928..e4cd1c3abd 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTests.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTests.kt @@ -22,12 +22,10 @@ import io.element.android.libraries.matrix.api.roomlist.RoomSummary import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID_2 import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled -import kotlinx.coroutines.CancellationException import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest -import kotlinx.coroutines.withTimeout import org.junit.Test import org.matrix.rustcomponents.sdk.RoomList import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate @@ -38,7 +36,6 @@ import org.matrix.rustcomponents.sdk.RoomListServiceInterface import org.matrix.rustcomponents.sdk.RoomListServiceStateListener import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicatorListener import org.matrix.rustcomponents.sdk.TaskHandle -import kotlin.time.Duration.Companion.milliseconds // NOTE: this class is using a fake implementation of a Rust SDK interface which returns actual Rust objects with pointers. // Since we don't access the data in those objects, this is fine for our tests, but that's as far as we can test this class. @@ -46,38 +43,11 @@ class RoomSummaryListProcessorTests { private val summaries = MutableStateFlow>(emptyList()) - @Test - fun `postUpdates can't start until postEntries is done`() = runTest { - val processor = createProcessor() - val update = listOf(RoomListEntriesUpdate.Reset(emptyList())) - - val timeoutError = runCatching { - withTimeout(10.milliseconds) { processor.postUpdate(update) } - }.exceptionOrNull() - assertThat(timeoutError).isInstanceOf(CancellationException::class.java) - - processor.postEntries(listOf(RoomListEntry.Empty)) - processor.postUpdate(update) - } - - @Test - fun `postEntries adds all new entries with no diffing`() = runTest { - summaries.value = listOf(aRoomSummaryFilled()) - val processor = createProcessor() - - processor.postEntries(listOf(RoomListEntry.Empty, RoomListEntry.Empty, RoomListEntry.Empty)) - - assertThat(summaries.value.count()).isEqualTo(4) - } - @Test fun `Append adds new entries at the end of the list`() = runTest { summaries.value = listOf(aRoomSummaryFilled()) val processor = createProcessor() - // Start processing updates - processor.postEntries(listOf()) - // Process actual update processor.postUpdate(listOf(RoomListEntriesUpdate.Append(listOf(RoomListEntry.Empty, RoomListEntry.Empty, RoomListEntry.Empty)))) assertThat(summaries.value.count()).isEqualTo(4) @@ -88,10 +58,6 @@ class RoomSummaryListProcessorTests { fun `PushBack adds a new entry at the end of the list`() = runTest { summaries.value = listOf(aRoomSummaryFilled()) val processor = createProcessor() - - // Start processing updates - processor.postEntries(listOf()) - // Process actual update processor.postUpdate(listOf(RoomListEntriesUpdate.PushBack(RoomListEntry.Empty))) assertThat(summaries.value.count()).isEqualTo(2) @@ -102,10 +68,6 @@ class RoomSummaryListProcessorTests { fun `PushFront inserts a new entry at the start of the list`() = runTest { summaries.value = listOf(aRoomSummaryFilled()) val processor = createProcessor() - - // Start processing updates - processor.postEntries(listOf()) - // Process actual update processor.postUpdate(listOf(RoomListEntriesUpdate.PushFront(RoomListEntry.Empty))) assertThat(summaries.value.count()).isEqualTo(2) @@ -118,9 +80,6 @@ class RoomSummaryListProcessorTests { val processor = createProcessor() val index = 0 - // Start processing updates - processor.postEntries(listOf()) - // Process actual update processor.postUpdate(listOf(RoomListEntriesUpdate.Set(index.toUInt(), RoomListEntry.Empty))) assertThat(summaries.value.count()).isEqualTo(1) @@ -133,9 +92,6 @@ class RoomSummaryListProcessorTests { val processor = createProcessor() val index = 0 - // Start processing updates - processor.postEntries(listOf()) - // Process actual update processor.postUpdate(listOf(RoomListEntriesUpdate.Insert(index.toUInt(), RoomListEntry.Empty))) assertThat(summaries.value.count()).isEqualTo(2) @@ -148,9 +104,6 @@ class RoomSummaryListProcessorTests { val processor = createProcessor() val index = 0 - // Start processing updates - processor.postEntries(listOf()) - // Process actual update processor.postUpdate(listOf(RoomListEntriesUpdate.Remove(index.toUInt()))) assertThat(summaries.value.count()).isEqualTo(1) @@ -163,9 +116,6 @@ class RoomSummaryListProcessorTests { val processor = createProcessor() val index = 0 - // Start processing updates - processor.postEntries(listOf()) - // Process actual update processor.postUpdate(listOf(RoomListEntriesUpdate.PopBack)) assertThat(summaries.value.count()).isEqualTo(1) @@ -178,9 +128,6 @@ class RoomSummaryListProcessorTests { val processor = createProcessor() val index = 0 - // Start processing updates - processor.postEntries(listOf()) - // Process actual update processor.postUpdate(listOf(RoomListEntriesUpdate.PopFront)) assertThat(summaries.value.count()).isEqualTo(1) @@ -192,9 +139,6 @@ class RoomSummaryListProcessorTests { summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2)) val processor = createProcessor() - // Start processing updates - processor.postEntries(listOf()) - // Process actual update processor.postUpdate(listOf(RoomListEntriesUpdate.Clear)) assertThat(summaries.value).isEmpty() @@ -206,9 +150,6 @@ class RoomSummaryListProcessorTests { val processor = createProcessor() val index = 0 - // Start processing updates - processor.postEntries(listOf()) - // Process actual update processor.postUpdate(listOf(RoomListEntriesUpdate.Truncate(1u))) assertThat(summaries.value.count()).isEqualTo(1) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt index cf5c3682c2..8d36c45bb6 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt @@ -17,17 +17,17 @@ package io.element.android.libraries.matrix.impl.timeline.postprocessor import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem -import io.element.android.libraries.matrix.test.room.anEventTimelineItem -import kotlinx.coroutines.flow.MutableStateFlow +import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Test import java.util.Date +private const val FAKE_UNIQUE_ID = "FAKE_UNIQUE_ID" + class TimelineEncryptedHistoryPostProcessorTest { private val defaultLastLoginTimestamp = Date(1_689_061_264L) @@ -36,7 +36,7 @@ class TimelineEncryptedHistoryPostProcessorTest { fun `given an unencrypted room, nothing is done`() = runTest { val processor = createPostProcessor(isRoomEncrypted = false) val items = listOf( - MatrixTimelineItem.Event(0L, anEventTimelineItem()) + MatrixTimelineItem.Event(FAKE_UNIQUE_ID, anEventTimelineItem()) ) assertThat(processor.process(items)).isSameInstanceAs(items) } @@ -45,7 +45,7 @@ class TimelineEncryptedHistoryPostProcessorTest { fun `given an encrypted room, and key backup enabled, nothing is done`() = runTest { val processor = createPostProcessor(isKeyBackupEnabled = true) val items = listOf( - MatrixTimelineItem.Event(0L, anEventTimelineItem()) + MatrixTimelineItem.Event(FAKE_UNIQUE_ID, anEventTimelineItem()) ) assertThat(processor.process(items)).isSameInstanceAs(items) } @@ -54,7 +54,7 @@ class TimelineEncryptedHistoryPostProcessorTest { fun `given a null lastLoginTimestamp, nothing is done`() = runTest { val processor = createPostProcessor(lastLoginTimestamp = null) val items = listOf( - MatrixTimelineItem.Event(0L, anEventTimelineItem()) + MatrixTimelineItem.Event(FAKE_UNIQUE_ID, anEventTimelineItem()) ) assertThat(processor.process(items)).isSameInstanceAs(items) } @@ -70,7 +70,7 @@ class TimelineEncryptedHistoryPostProcessorTest { fun `given a list with no items before lastLoginTimestamp, nothing is done`() = runTest { val processor = createPostProcessor() val items = listOf( - MatrixTimelineItem.Event(0L, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time + 1)) + MatrixTimelineItem.Event(FAKE_UNIQUE_ID, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time + 1)) ) assertThat(processor.process(items)).isSameInstanceAs(items) } @@ -79,49 +79,35 @@ class TimelineEncryptedHistoryPostProcessorTest { fun `given a list with an item with equal timestamp as lastLoginTimestamp, it's replaced`() = runTest { val processor = createPostProcessor() val items = listOf( - MatrixTimelineItem.Event(0L, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time)) + MatrixTimelineItem.Event(FAKE_UNIQUE_ID, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time)) ) assertThat(processor.process(items)) - .isEqualTo(listOf(MatrixTimelineItem.Virtual(0L, VirtualTimelineItem.EncryptedHistoryBanner))) + .isEqualTo(listOf(MatrixTimelineItem.Virtual(VirtualTimelineItem.EncryptedHistoryBanner.toString(), VirtualTimelineItem.EncryptedHistoryBanner))) } @Test fun `given a list with an item with a lower timestamp than lastLoginTimestamp, it's replaced`() = runTest { val processor = createPostProcessor() val items = listOf( - MatrixTimelineItem.Event(0L, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time - 1)) + MatrixTimelineItem.Event(FAKE_UNIQUE_ID, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time - 1)) ) assertThat(processor.process(items)).isEqualTo( - listOf(MatrixTimelineItem.Virtual(0L, VirtualTimelineItem.EncryptedHistoryBanner)) + listOf(MatrixTimelineItem.Virtual(VirtualTimelineItem.EncryptedHistoryBanner.toString(), VirtualTimelineItem.EncryptedHistoryBanner)) ) } @Test - fun `given a list with several with lower or equal timestamps than lastLoginTimestamp, they're replaced and the user can't back paginate`() = runTest { - val paginationStateFlow = MutableStateFlow( - MatrixTimeline.PaginationState( - hasMoreToLoadBackwards = true, - isBackPaginating = false, - beginningOfRoomReached = false, - ) - ) - val processor = createPostProcessor(paginationStateFlow = paginationStateFlow) + fun `given a list with several with lower or equal timestamps than lastLoginTimestamp, then they're replaced`() = runTest { + val processor = createPostProcessor() val items = listOf( - MatrixTimelineItem.Event(0L, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time - 1)), - MatrixTimelineItem.Event(0L, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time)), - MatrixTimelineItem.Event(0L, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time + 1)), + MatrixTimelineItem.Event(FAKE_UNIQUE_ID, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time - 1)), + MatrixTimelineItem.Event(FAKE_UNIQUE_ID, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time)), + MatrixTimelineItem.Event(FAKE_UNIQUE_ID, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time + 1)), ) assertThat(processor.process(items)).isEqualTo( listOf( - MatrixTimelineItem.Virtual(0L, VirtualTimelineItem.EncryptedHistoryBanner), - MatrixTimelineItem.Event(0L, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time + 1)) - ) - ) - assertThat(paginationStateFlow.value).isEqualTo( - MatrixTimeline.PaginationState( - hasMoreToLoadBackwards = false, - isBackPaginating = false, - beginningOfRoomReached = false, + MatrixTimelineItem.Virtual(VirtualTimelineItem.EncryptedHistoryBanner.toString(), VirtualTimelineItem.EncryptedHistoryBanner), + MatrixTimelineItem.Event(FAKE_UNIQUE_ID, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time + 1)) ) ) } @@ -130,19 +116,10 @@ class TimelineEncryptedHistoryPostProcessorTest { lastLoginTimestamp: Date? = defaultLastLoginTimestamp, isRoomEncrypted: Boolean = true, isKeyBackupEnabled: Boolean = false, - paginationStateFlow: MutableStateFlow = - MutableStateFlow( - MatrixTimeline.PaginationState( - hasMoreToLoadBackwards = true, - isBackPaginating = false, - beginningOfRoomReached = false, - ) - ) ) = TimelineEncryptedHistoryPostProcessor( lastLoginTimestamp = lastLoginTimestamp, isRoomEncrypted = isRoomEncrypted, isKeyBackupEnabled = isKeyBackupEnabled, - paginationStateFlow = paginationStateFlow, dispatcher = StandardTestDispatcher(testScheduler) ) } diff --git a/libraries/matrix/test/build.gradle.kts b/libraries/matrix/test/build.gradle.kts index 4e8893aab6..9c41948bd7 100644 --- a/libraries/matrix/test/build.gradle.kts +++ b/libraries/matrix/test/build.gradle.kts @@ -28,4 +28,5 @@ dependencies { api(libs.coroutines.core) implementation(libs.coroutines.test) implementation(projects.tests.testutils) + implementation(libs.kotlinx.collections.immutable) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index e8228e806e..3977fbb133 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -39,7 +39,6 @@ import io.element.android.libraries.matrix.test.media.FakeMediaLoader import io.element.android.libraries.matrix.test.notification.FakeNotificationService import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService import io.element.android.libraries.matrix.test.pushers.FakePushersService -import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import io.element.android.libraries.matrix.test.sync.FakeSyncService import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService @@ -72,8 +71,7 @@ class FakeMatrixClient( private var unignoreUserResult: Result = Result.success(Unit) private var createRoomResult: Result = Result.success(A_ROOM_ID) private var createDmResult: Result = Result.success(A_ROOM_ID) - private var createDmFailure: Throwable? = null - private var findDmResult: MatrixRoom? = FakeMatrixRoom() + private var findDmResult: RoomId? = A_ROOM_ID private var logoutFailure: Throwable? = null private val getRoomResults = mutableMapOf() private val searchUserResults = mutableMapOf>() @@ -87,7 +85,7 @@ class FakeMatrixClient( return getRoomResults[roomId] } - override suspend fun findDM(userId: UserId): MatrixRoom? { + override suspend fun findDM(userId: UserId): RoomId? { return findDmResult } @@ -99,14 +97,11 @@ class FakeMatrixClient( return unignoreUserResult } - override suspend fun createRoom(createRoomParams: CreateRoomParameters): Result { - delay(100) + override suspend fun createRoom(createRoomParams: CreateRoomParameters): Result = simulateLongTask { return createRoomResult } - override suspend fun createDM(userId: UserId): Result { - delay(100) - createDmFailure?.let { throw it } + override suspend fun createDM(userId: UserId): Result = simulateLongTask { return createDmResult } @@ -206,11 +201,7 @@ class FakeMatrixClient( unignoreUserResult = result } - fun givenCreateDmError(failure: Throwable?) { - createDmFailure = failure - } - - fun givenFindDmResult(result: MatrixRoom?) { + fun givenFindDmResult(result: RoomId?) { findDmResult = result } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt index 9f10f4ba35..44b9d734a5 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt @@ -30,10 +30,10 @@ class FakeEncryptionService : EncryptionService { private var disableRecoveryFailure: Exception? = null override val backupStateStateFlow: MutableStateFlow = MutableStateFlow(BackupState.UNKNOWN) override val recoveryStateStateFlow: MutableStateFlow = MutableStateFlow(RecoveryState.UNKNOWN) - override val enableRecoveryProgressStateFlow: MutableStateFlow = MutableStateFlow(EnableRecoveryProgress.Unknown) + override val enableRecoveryProgressStateFlow: MutableStateFlow = MutableStateFlow(EnableRecoveryProgress.Starting) private var waitForBackupUploadSteadyStateFlow: Flow = flowOf() - private var fixRecoveryIssuesFailure: Exception? = null + private var recoverFailure: Exception? = null private var doesBackupExistOnServerResult: Result = Result.success(true) override suspend fun enableBackups(): Result = simulateLongTask { @@ -44,8 +44,8 @@ class FakeEncryptionService : EncryptionService { disableRecoveryFailure = exception } - fun givenFixRecoveryIssuesFailure(exception: Exception?) { - fixRecoveryIssuesFailure = exception + fun givenRecoverFailure(exception: Exception?) { + recoverFailure = exception } override suspend fun disableRecovery(): Result = simulateLongTask { @@ -61,8 +61,8 @@ class FakeEncryptionService : EncryptionService { return doesBackupExistOnServerResult } - override suspend fun fixRecoveryIssues(recoveryKey: String): Result = simulateLongTask { - fixRecoveryIssuesFailure?.let { return Result.failure(it) } + override suspend fun recover(recoveryKey: String): Result = simulateLongTask { + recoverFailure?.let { return Result.failure(it) } return Result.success(Unit) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/FakeNotificationService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/FakeNotificationService.kt index 7cb92d35c5..31fc10f342 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/FakeNotificationService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/FakeNotificationService.kt @@ -23,7 +23,17 @@ import io.element.android.libraries.matrix.api.notification.NotificationData import io.element.android.libraries.matrix.api.notification.NotificationService class FakeNotificationService : NotificationService { - override suspend fun getNotification(userId: SessionId, roomId: RoomId, eventId: EventId): Result { - return Result.success(null) + private var getNotificationResult: Result = Result.success(null) + + fun givenGetNotificationResult(result: Result) { + getNotificationResult = result + } + + override suspend fun getNotification( + userId: SessionId, + roomId: RoomId, + eventId: EventId, + ): Result { + return getNotificationResult } } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt index 039b855e7e..68bc508723 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt @@ -41,18 +41,20 @@ class FakeNotificationSettingsService( private var roomNotificationMode: RoomNotificationMode = initialRoomMode private var roomNotificationModeIsDefault: Boolean = initialRoomModeIsDefault private var callNotificationsEnabled = false + private var inviteNotificationsEnabled = false private var atRoomNotificationsEnabled = false private var setNotificationModeError: Throwable? = null private var restoreDefaultNotificationModeError: Throwable? = null private var setDefaultNotificationModeError: Throwable? = null private var setAtRoomError: Throwable? = null + private var canHomeServerPushEncryptedEventsToDeviceResult = Result.success(true) override val notificationSettingsChangeFlow: SharedFlow get() = _notificationSettingsStateFlow override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result { return Result.success( RoomNotificationSettings( - mode = if(roomNotificationModeIsDefault) defaultEncryptedGroupRoomNotificationMode else roomNotificationMode, + mode = if (roomNotificationModeIsDefault) defaultEncryptedGroupRoomNotificationMode else roomNotificationMode, isDefault = roomNotificationModeIsDefault ) ) @@ -149,10 +151,23 @@ class FakeNotificationSettingsService( return Result.success(Unit) } + override suspend fun isInviteForMeEnabled(): Result { + return Result.success(inviteNotificationsEnabled) + } + + override suspend fun setInviteForMeEnabled(enabled: Boolean): Result { + inviteNotificationsEnabled = enabled + return Result.success(Unit) + } + override suspend fun getRoomsWithUserDefinedRules(): Result> { return Result.success(if (roomNotificationModeIsDefault) listOf() else listOf(A_ROOM_ID.value)) } + override suspend fun canHomeServerPushEncryptedEventsToDevice(): Result { + return canHomeServerPushEncryptedEventsToDeviceResult + } + fun givenSetNotificationModeError(throwable: Throwable?) { setNotificationModeError = throwable } @@ -169,4 +184,7 @@ class FakeNotificationSettingsService( setDefaultNotificationModeError = throwable } + fun givenCanHomeServerPushEncryptedEventsToDeviceResult(result: Result) { + canHomeServerPushEncryptedEventsToDeviceResult = result + } } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index a041263e2c..9f036a6b62 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -53,6 +53,7 @@ import io.element.android.libraries.matrix.test.notificationsettings.FakeNotific import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline import io.element.android.libraries.matrix.test.widget.FakeWidgetDriver import io.element.android.tests.testutils.simulateLongTask +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow @@ -103,12 +104,14 @@ class FakeMatrixRoom( private var reportContentResult = Result.success(Unit) private var sendLocationResult = Result.success(Unit) private var createPollResult = Result.success(Unit) + private var editPollResult = Result.success(Unit) private var sendPollResponseResult = Result.success(Unit) private var endPollResult = Result.success(Unit) private var progressCallbackValues = emptyList>() private var generateWidgetWebViewUrlResult = Result.success("https://call.element.io") private var getWidgetDriverResult: Result = Result.success(FakeWidgetDriver()) private var canUserTriggerRoomNotificationResult: Result = Result.success(true) + private var canUserJoinCallResult: Result = Result.success(true) var sendMessageMentions = emptyList() val editMessageCalls = mutableListOf>() @@ -130,8 +133,11 @@ class FakeMatrixRoom( private val _sentLocations = mutableListOf() val sentLocations: List = _sentLocations - private val _createPollInvocations = mutableListOf() - val createPollInvocations: List = _createPollInvocations + private val _createPollInvocations = mutableListOf() + val createPollInvocations: List = _createPollInvocations + + private val _editPollInvocations = mutableListOf() + val editPollInvocations: List = _editPollInvocations private val _sendPollResponseInvocations = mutableListOf() val sendPollResponseInvocations: List = _sendPollResponseInvocations @@ -287,16 +293,20 @@ class FakeMatrixRoom( return canUserTriggerRoomNotificationResult } + override suspend fun canUserJoinCall(userId: UserId): Result { + return canUserJoinCallResult + } + override suspend fun sendImage( file: File, - thumbnailFile: File, + thumbnailFile: File?, imageInfo: ImageInfo, progressCallback: ProgressCallback? ): Result = fakeSendMedia(progressCallback) override suspend fun sendVideo( file: File, - thumbnailFile: File, + thumbnailFile: File?, videoInfo: VideoInfo, progressCallback: ProgressCallback? ): Result = fakeSendMedia( @@ -375,10 +385,21 @@ class FakeMatrixRoom( maxSelections: Int, pollKind: PollKind ): Result = simulateLongTask { - _createPollInvocations.add(CreatePollInvocation(question, answers, maxSelections, pollKind)) + _createPollInvocations.add(SavePollInvocation(question, answers, maxSelections, pollKind)) return createPollResult } + override suspend fun editPoll( + pollStartId: EventId, + question: String, + answers: List, + maxSelections: Int, + pollKind: PollKind + ): Result = simulateLongTask { + _editPollInvocations.add(SavePollInvocation(question, answers, maxSelections, pollKind)) + return editPollResult + } + override suspend fun sendPollResponse( pollStartId: EventId, answers: List @@ -411,6 +432,10 @@ class FakeMatrixRoom( override fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result = getWidgetDriverResult + override fun pollHistory(): MatrixTimeline { + return FakeMatrixTimeline() + } + fun givenLeaveRoomError(throwable: Throwable?) { this.leaveRoomError = throwable } @@ -455,6 +480,10 @@ class FakeMatrixRoom( canUserTriggerRoomNotificationResult = result } + fun givenCanUserJoinCall(result: Result) { + canUserJoinCallResult = result + } + fun givenIgnoreResult(result: Result) { ignoreResult = result } @@ -511,6 +540,10 @@ class FakeMatrixRoom( createPollResult = result } + fun givenEditPollResult(result: Result) { + editPollResult = result + } + fun givenSendPollResponseResult(result: Result) { sendPollResponseResult = result } @@ -544,7 +577,7 @@ data class SendLocationInvocation( val assetType: AssetType?, ) -data class CreatePollInvocation( +data class SavePollInvocation( val question: String, val answers: List, val maxSelections: Int, @@ -593,7 +626,7 @@ fun aRoomInfo( isSpace = isSpace, isTombstoned = isTombstoned, canonicalAlias = canonicalAlias, - alternativeAliases = alternativeAliases, + alternativeAliases = alternativeAliases.toImmutableList(), currentUserMembership = currentUserMembership, latestEvent = latestEvent, inviter = inviter, @@ -604,5 +637,5 @@ fun aRoomInfo( notificationCount = notificationCount, userDefinedNotificationMode = userDefinedNotificationMode, hasRoomCall = hasRoomCall, - activeRoomCallParticipants = activeRoomCallParticipants + activeRoomCallParticipants = activeRoomCallParticipants.toImmutableList(), ) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt index d12f168789..79d3acb58b 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt @@ -18,32 +18,17 @@ package io.element.android.libraries.matrix.test.room import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.api.poll.PollAnswer -import io.element.android.libraries.matrix.api.poll.PollKind import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.message.RoomMessage import io.element.android.libraries.matrix.api.roomlist.RoomSummary import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails -import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo -import io.element.android.libraries.matrix.api.timeline.item.event.EventContent -import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem -import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo -import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState -import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent -import io.element.android.libraries.matrix.api.timeline.item.event.MessageType -import io.element.android.libraries.matrix.api.timeline.item.event.PollContent -import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent -import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails -import io.element.android.libraries.matrix.api.timeline.item.event.Receipt -import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_NAME import io.element.android.libraries.matrix.test.A_USER_ID -import io.element.android.libraries.matrix.test.A_USER_NAME +import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem fun aRoomSummaryFilled( roomId: RoomId = A_ROOM_ID, @@ -98,94 +83,3 @@ fun aRoomMessage( sender = userId, originServerTs = timestamp, ) - -fun anEventTimelineItem( - eventId: EventId = AN_EVENT_ID, - transactionId: TransactionId? = null, - isEditable: Boolean = false, - isLocal: Boolean = false, - isOwn: Boolean = false, - isRemote: Boolean = false, - localSendState: LocalEventSendState? = null, - reactions: List = emptyList(), - receipts: List = emptyList(), - sender: UserId = A_USER_ID, - senderProfile: ProfileTimelineDetails = aProfileTimelineDetails(), - timestamp: Long = 0L, - content: EventContent = aProfileChangeMessageContent(), - debugInfo: TimelineItemDebugInfo = aTimelineItemDebugInfo(), -) = EventTimelineItem( - eventId = eventId, - transactionId = transactionId, - isEditable = isEditable, - isLocal = isLocal, - isOwn = isOwn, - isRemote = isRemote, - localSendState = localSendState, - reactions = reactions, - receipts = receipts, - sender = sender, - senderProfile = senderProfile, - timestamp = timestamp, - content = content, - debugInfo = debugInfo, - origin = null, -) - -fun aProfileTimelineDetails( - displayName: String? = A_USER_NAME, - displayNameAmbiguous: Boolean = false, - avatarUrl: String? = null -): ProfileTimelineDetails = ProfileTimelineDetails.Ready( - displayName = displayName, - displayNameAmbiguous = displayNameAmbiguous, - avatarUrl = avatarUrl, -) - -fun aProfileChangeMessageContent( - displayName: String? = null, - prevDisplayName: String? = null, - avatarUrl: String? = null, - prevAvatarUrl: String? = null, -) = ProfileChangeContent( - displayName = displayName, - prevDisplayName = prevDisplayName, - avatarUrl = avatarUrl, - prevAvatarUrl = prevAvatarUrl, -) - -fun aMessageContent( - body: String = "body", - inReplyTo: InReplyTo? = null, - isEdited: Boolean = false, - isThreaded: Boolean = false, - messageType: MessageType = TextMessageType( - body = body, - formatted = null - ) -) = MessageContent( - body = body, - inReplyTo = inReplyTo, - isEdited = isEdited, - isThreaded = isThreaded, - type = messageType -) - -fun aTimelineItemDebugInfo( - model: String = "Rust(Model())", - originalJson: String? = null, - latestEditedJson: String? = null, -) = TimelineItemDebugInfo( - model, originalJson, latestEditedJson -) - -fun aPollContent( - question: String = "Do you like polls?", -) = PollContent( - question = question, - kind = PollKind.Disclosed, - maxSelections = 1u, - answers = listOf(PollAnswer("1", "Yes"), PollAnswer("2", "No")), - votes = mapOf(), - endTime = null -) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt index 75a91508d0..073a314e9f 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt @@ -16,6 +16,7 @@ package io.element.android.libraries.matrix.test.roomlist +import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList import io.element.android.libraries.matrix.api.roomlist.RoomList import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.RoomSummary @@ -51,31 +52,29 @@ class FakeRoomListService : RoomListService { roomListStateFlow.emit(state) } + suspend fun postSyncIndicator(value: RoomListService.SyncIndicator) { + syncIndicatorStateFlow.emit(value) + } + var latestSlidingSyncRange: IntRange? = null private set + override val allRooms: DynamicRoomList = SimplePagedRoomList( + allRoomSummariesFlow, + allRoomsLoadingStateFlow, + MutableStateFlow(DynamicRoomList.Filter.None) + ) + + override val invites: RoomList = SimplePagedRoomList( + inviteRoomSummariesFlow, + inviteRoomsLoadingStateFlow, + MutableStateFlow(DynamicRoomList.Filter.None) + ) + override fun updateAllRoomsVisibleRange(range: IntRange) { latestSlidingSyncRange = range } - override fun rebuildRoomSummaries() { - - } - - override fun allRooms(): RoomList { - return SimpleRoomList( - summaries = allRoomSummariesFlow, - loadingState = allRoomsLoadingStateFlow - ) - } - - override fun invites(): RoomList { - return SimpleRoomList( - summaries = inviteRoomSummariesFlow, - loadingState = inviteRoomsLoadingStateFlow - ) - } - override val state: StateFlow = roomListStateFlow override val syncIndicator: StateFlow = syncIndicatorStateFlow diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimplePagedRoomList.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimplePagedRoomList.kt new file mode 100644 index 0000000000..e94002bd1d --- /dev/null +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimplePagedRoomList.kt @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrix.test.roomlist + +import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList +import io.element.android.libraries.matrix.api.roomlist.RoomList +import io.element.android.libraries.matrix.api.roomlist.RoomSummary +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.getAndUpdate + +data class SimplePagedRoomList( + override val summaries: StateFlow>, + override val loadingState: StateFlow, + override val currentFilter: MutableStateFlow +) : DynamicRoomList { + + override val pageSize: Int = Int.MAX_VALUE + override val loadedPages = MutableStateFlow(1) + + override suspend fun loadMore() { + //No-op + loadedPages.getAndUpdate { it + 1 } + } + + override suspend fun reset() { + loadedPages.emit(1) + } + + override suspend fun updateFilter(filter: DynamicRoomList.Filter) { + currentFilter.emit(filter) + } + + override suspend fun rebuildSummaries() { + //No-op + } +} diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeMatrixTimeline.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeMatrixTimeline.kt index 6cf6f05cbe..94a8de2ced 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeMatrixTimeline.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeMatrixTimeline.kt @@ -19,6 +19,7 @@ package io.element.android.libraries.matrix.test.timeline import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.tests.testutils.simulateLongTask import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.delay @@ -56,7 +57,10 @@ class FakeMatrixTimeline( override val timelineItems: Flow> = _timelineItems - override suspend fun paginateBackwards(requestSize: Int, untilNumberOfItems: Int): Result { + override suspend fun paginateBackwards(requestSize: Int) = paginateBackwards() + override suspend fun paginateBackwards(requestSize: Int, untilNumberOfItems: Int) = paginateBackwards() + + private suspend fun paginateBackwards(): Result { updatePaginationState { copy(isBackPaginating = true) } @@ -74,9 +78,14 @@ class FakeMatrixTimeline( Result.success(Unit) } - override suspend fun sendReadReceipt(eventId: EventId): Result = simulateLongTask { + override suspend fun sendReadReceipt( + eventId: EventId, + receiptType: ReceiptType, + ): Result = simulateLongTask { sendReadReceiptCount++ sendReadReceiptLatch?.complete(Unit) Result.success(Unit) } + + override fun close() = Unit } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt new file mode 100644 index 0000000000..0cbbe81eef --- /dev/null +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrix.test.timeline + +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.TransactionId +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.poll.PollAnswer +import io.element.android.libraries.matrix.api.poll.PollKind +import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo +import io.element.android.libraries.matrix.api.timeline.item.event.EventContent +import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction +import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo +import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState +import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.MessageType +import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent +import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails +import io.element.android.libraries.matrix.api.timeline.item.event.Receipt +import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.A_USER_NAME +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.ImmutableMap +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentMapOf + +fun anEventTimelineItem( + eventId: EventId = AN_EVENT_ID, + transactionId: TransactionId? = null, + isEditable: Boolean = false, + isLocal: Boolean = false, + isOwn: Boolean = false, + isRemote: Boolean = false, + localSendState: LocalEventSendState? = null, + reactions: ImmutableList = persistentListOf(), + receipts: ImmutableList = persistentListOf(), + sender: UserId = A_USER_ID, + senderProfile: ProfileTimelineDetails = aProfileTimelineDetails(), + timestamp: Long = 0L, + content: EventContent = aProfileChangeMessageContent(), + debugInfo: TimelineItemDebugInfo = aTimelineItemDebugInfo(), +) = EventTimelineItem( + eventId = eventId, + transactionId = transactionId, + isEditable = isEditable, + isLocal = isLocal, + isOwn = isOwn, + isRemote = isRemote, + localSendState = localSendState, + reactions = reactions, + receipts = receipts, + sender = sender, + senderProfile = senderProfile, + timestamp = timestamp, + content = content, + debugInfo = debugInfo, + origin = null, +) + +fun aProfileTimelineDetails( + displayName: String? = A_USER_NAME, + displayNameAmbiguous: Boolean = false, + avatarUrl: String? = null +): ProfileTimelineDetails = ProfileTimelineDetails.Ready( + displayName = displayName, + displayNameAmbiguous = displayNameAmbiguous, + avatarUrl = avatarUrl, +) + +fun aProfileChangeMessageContent( + displayName: String? = null, + prevDisplayName: String? = null, + avatarUrl: String? = null, + prevAvatarUrl: String? = null, +) = ProfileChangeContent( + displayName = displayName, + prevDisplayName = prevDisplayName, + avatarUrl = avatarUrl, + prevAvatarUrl = prevAvatarUrl, +) + +fun aMessageContent( + body: String = "body", + inReplyTo: InReplyTo? = null, + isEdited: Boolean = false, + isThreaded: Boolean = false, + messageType: MessageType = TextMessageType( + body = body, + formatted = null + ) +) = MessageContent( + body = body, + inReplyTo = inReplyTo, + isEdited = isEdited, + isThreaded = isThreaded, + type = messageType +) + +fun aTimelineItemDebugInfo( + model: String = "Rust(Model())", + originalJson: String? = null, + latestEditedJson: String? = null, +) = TimelineItemDebugInfo( + model, originalJson, latestEditedJson +) + +fun aPollContent( + question: String = "Do you like polls?", + answers: ImmutableList = persistentListOf(PollAnswer("1", "Yes"), PollAnswer("2", "No")), + kind: PollKind = PollKind.Disclosed, + maxSelections: ULong = 1u, + votes: ImmutableMap> = persistentMapOf(), + endTime: ULong? = null, + isEdited: Boolean = false, +) = PollContent( + question = question, + kind = kind, + maxSelections = maxSelections, + answers = answers, + votes = votes, + endTime = endTime, + isEdited = isEdited, +) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/verification/FakeSessionVerificationService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/verification/FakeSessionVerificationService.kt index 62b0b39adf..4a7aa1c304 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/verification/FakeSessionVerificationService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/verification/FakeSessionVerificationService.kt @@ -16,10 +16,10 @@ package io.element.android.libraries.matrix.test.verification +import io.element.android.libraries.matrix.api.verification.SessionVerificationData import io.element.android.libraries.matrix.api.verification.SessionVerificationService -import io.element.android.libraries.matrix.api.verification.VerificationFlowState import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus -import io.element.android.libraries.matrix.api.verification.VerificationEmoji +import io.element.android.libraries.matrix.api.verification.VerificationFlowState import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -29,10 +29,9 @@ class FakeSessionVerificationService : SessionVerificationService { private val _sessionVerifiedStatus = MutableStateFlow(SessionVerifiedStatus.Unknown) private var _verificationFlowState = MutableStateFlow(VerificationFlowState.Initial) private var _canVerifySessionFlow = MutableStateFlow(true) - private var emojiList = emptyList() var shouldFail = false - override val verificationFlowState: StateFlow =_verificationFlowState + override val verificationFlowState: StateFlow = _verificationFlowState override val sessionVerifiedStatus: StateFlow = _sessionVerifiedStatus override val canVerifySessionFlow: Flow = _canVerifySessionFlow @@ -62,8 +61,8 @@ class FakeSessionVerificationService : SessionVerificationService { } } - fun triggerReceiveVerificationData() { - _verificationFlowState.value = VerificationFlowState.ReceivedVerificationData(emojiList) + fun triggerReceiveVerificationData(sessionVerificationData: SessionVerificationData) { + _verificationFlowState.value = VerificationFlowState.ReceivedVerificationData(sessionVerificationData) } override suspend fun startVerification() { @@ -86,10 +85,6 @@ class FakeSessionVerificationService : SessionVerificationService { _isReady.value = value } - fun givenEmojiList(emojis: List) { - this.emojiList = emojis - } - override suspend fun reset() { _verificationFlowState.value = VerificationFlowState.Initial } diff --git a/libraries/matrixui/build.gradle.kts b/libraries/matrixui/build.gradle.kts index 7302eeddfb..669f9c9634 100644 --- a/libraries/matrixui/build.gradle.kts +++ b/libraries/matrixui/build.gradle.kts @@ -40,6 +40,11 @@ dependencies { implementation(projects.libraries.uiStrings) implementation(libs.coil.compose) implementation(libs.coil.gif) + implementation(libs.jsoup) ksp(libs.showkase.processor) + + testImplementation(libs.test.junit) + testImplementation(libs.test.truth) + testImplementation(libs.test.robolectric) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnail.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnail.kt index 40c58fb578..e0f3e7748c 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnail.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnail.kt @@ -36,6 +36,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.BlurHashAsyncImage import io.element.android.libraries.designsystem.components.PinIcon import io.element.android.libraries.designsystem.preview.ElementPreview @@ -122,6 +123,12 @@ fun AttachmentThumbnail( ) */ } + AttachmentThumbnailType.Poll -> { + Icon( + imageVector = CompoundIcons.Polls, + contentDescription = info.textContent, + ) + } } } } @@ -129,7 +136,7 @@ fun AttachmentThumbnail( @Parcelize enum class AttachmentThumbnailType : Parcelable { - Image, Video, File, Audio, Location, Voice + Image, Video, File, Audio, Location, Voice, Poll } @Parcelize diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnailInfoProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnailInfoProvider.kt index 83d1a84973..194e14de76 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnailInfoProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnailInfoProvider.kt @@ -30,6 +30,7 @@ open class AttachmentThumbnailInfoProvider : PreviewParameterProvider, modalBottomSheetState: ModalBottomSheetState, + onActionSelected: (action: AvatarAction) -> Unit, modifier: Modifier = Modifier, - onActionSelected: (action: AvatarAction) -> Unit = {}, ) { val coroutineScope = rememberCoroutineScope() fun onItemActionClicked(itemAction: AvatarAction) { @@ -119,5 +119,6 @@ internal fun AvatarActionBottomSheetPreview() = ElementPreview { initialValue = ModalBottomSheetValue.Expanded, density = LocalDensity.current, ), + onActionSelected = { }, ) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt index d9bbff4f7d..805e240194 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt @@ -21,8 +21,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.model.getAvatarData import io.element.android.libraries.matrix.ui.model.getBestName @@ -31,9 +31,9 @@ import io.element.android.libraries.matrix.ui.model.getBestName fun CheckableMatrixUserRow( checked: Boolean, matrixUser: MatrixUser, + onCheckedChange: (Boolean) -> Unit, modifier: Modifier = Modifier, avatarSize: AvatarSize = AvatarSize.UserListItem, - onCheckedChange: (Boolean) -> Unit = {}, enabled: Boolean = true, ) = CheckableUserRow( checked = checked, @@ -49,9 +49,27 @@ fun CheckableMatrixUserRow( @Composable internal fun CheckableMatrixUserRowPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreview { Column { - CheckableMatrixUserRow(checked = true, matrixUser) - CheckableMatrixUserRow(checked = false, matrixUser) - CheckableMatrixUserRow(checked = true, matrixUser, enabled = false) - CheckableMatrixUserRow(checked = false, matrixUser, enabled = false) + CheckableMatrixUserRow( + checked = true, + onCheckedChange = { }, + matrixUser = matrixUser, + ) + CheckableMatrixUserRow( + checked = false, + onCheckedChange = { }, + matrixUser = matrixUser, + ) + CheckableMatrixUserRow( + checked = true, + onCheckedChange = { }, + matrixUser = matrixUser, + enabled = false, + ) + CheckableMatrixUserRow( + checked = false, + onCheckedChange = { }, + matrixUser = matrixUser, + enabled = false, + ) } } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableUnresolvedUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableUnresolvedUserRow.kt index b829ff8e47..125b9b13f6 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableUnresolvedUserRow.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableUnresolvedUserRow.kt @@ -37,10 +37,10 @@ import io.element.android.libraries.matrix.ui.model.getAvatarData @Composable fun CheckableUnresolvedUserRow( checked: Boolean, + onCheckedChange: (Boolean) -> Unit, avatarData: AvatarData, id: String, modifier: Modifier = Modifier, - onCheckedChange: (Boolean) -> Unit = {}, enabled: Boolean = true, ) { Row( @@ -71,12 +71,34 @@ fun CheckableUnresolvedUserRow( internal fun CheckableUnresolvedUserRowPreview() = ElementThemedPreview { val matrixUser = aMatrixUser() Column { - CheckableUnresolvedUserRow(false, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value) + CheckableUnresolvedUserRow( + checked = false, + onCheckedChange = { }, + avatarData = matrixUser.getAvatarData(AvatarSize.UserListItem), + id = matrixUser.userId.value, + ) HorizontalDivider() - CheckableUnresolvedUserRow(true, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value) + CheckableUnresolvedUserRow( + checked = true, + onCheckedChange = { }, + avatarData = matrixUser.getAvatarData(AvatarSize.UserListItem), + id = matrixUser.userId.value, + ) HorizontalDivider() - CheckableUnresolvedUserRow(false, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value, enabled = false) + CheckableUnresolvedUserRow( + checked = false, + onCheckedChange = { }, + avatarData = matrixUser.getAvatarData(AvatarSize.UserListItem), + id = matrixUser.userId.value, + enabled = false, + ) HorizontalDivider() - CheckableUnresolvedUserRow(true, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value, enabled = false) + CheckableUnresolvedUserRow( + checked = true, + onCheckedChange = { }, + avatarData = matrixUser.getAvatarData(AvatarSize.UserListItem), + id = matrixUser.userId.value, + enabled = false, + ) } } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableUserRow.kt index f6272b9fda..1b7ead9131 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableUserRow.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableUserRow.kt @@ -31,11 +31,11 @@ import io.element.android.libraries.designsystem.theme.components.Checkbox @Composable fun CheckableUserRow( checked: Boolean, + onCheckedChange: (Boolean) -> Unit, avatarData: AvatarData, name: String, subtext: String?, modifier: Modifier = Modifier, - onCheckedChange: (Boolean) -> Unit = {}, enabled: Boolean = true, ) { Row( diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt index 289c37530a..e28435a243 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt @@ -87,7 +87,7 @@ fun EditableAvatarView( Icon( modifier = Modifier.size(16.dp), resourceId = CommonDrawables.ic_edit, - contentDescription = "", + contentDescription = null, tint = MaterialTheme.colorScheme.onPrimary, ) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt index 901b403d0f..8a00be4663 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt @@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp @@ -36,14 +37,14 @@ import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.model.getAvatarData import io.element.android.libraries.matrix.ui.model.getBestName -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable fun MatrixUserHeader( matrixUser: MatrixUser?, modifier: Modifier = Modifier, // TODO handle click on this item, to let the user be able to update their profile. - // onClick: () -> Unit = {}, + // onClick: () -> Unit, ) { if (matrixUser == null) { MatrixUserHeaderPlaceholder(modifier = modifier) @@ -60,7 +61,7 @@ fun MatrixUserHeader( private fun MatrixUserHeaderContent( matrixUser: MatrixUser, modifier: Modifier = Modifier, - // onClick: () -> Unit = {}, + // onClick: () -> Unit, ) { Row( modifier = modifier @@ -80,6 +81,7 @@ private fun MatrixUserHeaderContent( ) { // Name Text( + modifier = Modifier.clipToBounds(), text = matrixUser.getBestName(), maxLines = 1, style = ElementTheme.typography.fontHeadingSmMedium, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt index ff0e674a79..0df8b027f9 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt @@ -36,7 +36,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.theme.placeholderBackground -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable fun MatrixUserHeaderPlaceholder( diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt index dbf4968ba3..02767e10bb 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt @@ -34,15 +34,15 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails import io.element.android.libraries.ui.strings.CommonStrings @@ -50,8 +50,8 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun SelectedRoom( roomSummary: RoomSummaryDetails, + onRoomRemoved: (RoomSummaryDetails) -> Unit, modifier: Modifier = Modifier, - onRoomRemoved: (RoomSummaryDetails) -> Unit = {}, ) { Box( modifier = modifier @@ -81,7 +81,7 @@ fun SelectedRoom( ), ) { Icon( - resourceId = CommonDrawables.ic_compound_close, + imageVector = CompoundIcons.Close, contentDescription = stringResource(id = CommonStrings.action_remove), tint = MaterialTheme.colorScheme.onPrimary, modifier = Modifier.padding(2.dp) @@ -104,6 +104,7 @@ internal fun SelectedRoomPreview() = ElementPreview { lastMessageTimestamp = null, unreadNotificationCount = 0, inviter = null, - ) + ), + onRoomRemoved = {}, ) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUser.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUser.kt index 536ef783ff..a3199369e2 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUser.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUser.kt @@ -31,17 +31,18 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.model.getAvatarData import io.element.android.libraries.matrix.ui.model.getBestName @@ -50,8 +51,8 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun SelectedUser( matrixUser: MatrixUser, + onUserRemoved: (MatrixUser) -> Unit, modifier: Modifier = Modifier, - onUserRemoved: (MatrixUser) -> Unit = {}, ) { Box( modifier = modifier @@ -62,6 +63,7 @@ fun SelectedUser( ) { Avatar(matrixUser.getAvatarData(size = AvatarSize.SelectedUser)) Text( + modifier = Modifier.clipToBounds(), text = matrixUser.getBestName(), overflow = TextOverflow.Ellipsis, maxLines = 1, @@ -81,7 +83,7 @@ fun SelectedUser( ), ) { Icon( - resourceId = CommonDrawables.ic_compound_close, + imageVector = CompoundIcons.Close, contentDescription = stringResource(id = CommonStrings.action_remove), tint = MaterialTheme.colorScheme.onPrimary, modifier = Modifier.padding(2.dp) @@ -93,5 +95,8 @@ fun SelectedUser( @PreviewsDayNight @Composable internal fun SelectedUserPreview() = ElementPreview { - SelectedUser(aMatrixUser()) + SelectedUser( + aMatrixUser(), + onUserRemoved = {}, + ) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt index 884f45de02..e3aab57059 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt @@ -37,8 +37,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.Layout import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.text.toPx import io.element.android.libraries.matrix.api.user.MatrixUser import kotlinx.collections.immutable.ImmutableList @@ -48,10 +48,10 @@ import kotlin.math.floor @Composable fun SelectedUsersList( selectedUsers: ImmutableList, + onUserRemoved: (MatrixUser) -> Unit, modifier: Modifier = Modifier, autoScroll: Boolean = false, contentPadding: PaddingValues = PaddingValues(0.dp), - onUserRemoved: (MatrixUser) -> Unit = {}, ) { val lazyListState = rememberLazyListState() if (autoScroll) { @@ -135,6 +135,7 @@ internal fun SelectedUsersListPreview() = ElementPreview { // Two users that will be visible with no scrolling SelectedUsersList( selectedUsers = aMatrixUserList().take(2).toImmutableList(), + onUserRemoved = {}, modifier = Modifier .width(200.dp) .border(1.dp, Color.Red) @@ -144,6 +145,7 @@ internal fun SelectedUsersListPreview() = ElementPreview { for (i in 0..5) { SelectedUsersList( selectedUsers = aMatrixUserList().take(6).toImmutableList(), + onUserRemoved = {}, modifier = Modifier .width((200 + i * 20).dp) .border(1.dp, Color.Red) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnresolvedUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnresolvedUserRow.kt index 2916fa7f06..33f5d37057 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnresolvedUserRow.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnresolvedUserRow.kt @@ -32,15 +32,15 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.ui.model.getAvatarData -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -78,8 +78,8 @@ fun UnresolvedUserRow( .padding(top = 3.dp) ) { Icon( - resourceId = CommonDrawables.ic_compound_error, - contentDescription = "", + imageVector = CompoundIcons.Error, + contentDescription = null, modifier = Modifier .size(18.dp) .align(Alignment.Top) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt index f7852d690e..0303e2eb6c 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt @@ -40,7 +40,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.debugPlaceholderBackground import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.temporaryColorBgSpecial -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme /** * An avatar that the user has selected, but which has not yet been uploaded to Matrix. @@ -72,7 +72,7 @@ fun UnsavedAvatar( Box(modifier = commonModifier.background(ElementTheme.colors.temporaryColorBgSpecial)) { Icon( imageVector = Icons.Outlined.AddAPhoto, - contentDescription = "", + contentDescription = null, modifier = Modifier .align(Alignment.Center) .size(40.dp), diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UserRow.kt index 7aa11ba193..f9eef6b990 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UserRow.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UserRow.kt @@ -25,12 +25,13 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme @Composable internal fun UserRow( @@ -55,6 +56,7 @@ internal fun UserRow( ) { // Name Text( + modifier = Modifier.clipToBounds(), text = name, maxLines = 1, overflow = TextOverflow.Ellipsis, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarAction.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarAction.kt index e5305b86ae..eb8176340c 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarAction.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarAction.kt @@ -19,6 +19,7 @@ package io.element.android.libraries.matrix.ui.media import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.compose.runtime.Immutable +import io.element.android.libraries.designsystem.icons.CompoundDrawables import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.ui.strings.CommonStrings @@ -40,7 +41,7 @@ sealed class AvatarAction( data object Remove : AvatarAction( titleResId = CommonStrings.action_remove, - iconResourceId = CommonDrawables.ic_compound_delete, + iconResourceId = CompoundDrawables.ic_delete, destructive = true ) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt index c495558fd9..4c5309e2bd 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt @@ -28,8 +28,8 @@ import okhttp3.OkHttpClient import javax.inject.Inject import javax.inject.Provider -class LoggedInImageLoaderFactory @Inject constructor( - @ApplicationContext private val context: Context, +class LoggedInImageLoaderFactory( + private val context: Context, private val matrixClient: MatrixClient, private val okHttpClient: Provider, ) : ImageLoaderFactory { diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt new file mode 100644 index 0000000000..fc027d0e5c --- /dev/null +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrix.ui.media + +import android.content.Context +import coil.ImageLoader +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.ApplicationContext +import io.element.android.libraries.di.SingleIn +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.sessionstorage.api.observer.SessionListener +import io.element.android.libraries.sessionstorage.api.observer.SessionObserver +import okhttp3.OkHttpClient +import javax.inject.Inject +import javax.inject.Provider + +interface ImageLoaderHolder { + fun get(client: MatrixClient): ImageLoader +} + +@ContributesBinding(AppScope::class) +@SingleIn(AppScope::class) +class DefaultImageLoaderHolder @Inject constructor( + @ApplicationContext private val context: Context, + private val okHttpClient: Provider, + private val sessionObserver: SessionObserver, +) : ImageLoaderHolder { + private val map = mutableMapOf() + + init { + observeSessions() + } + + private fun observeSessions() { + sessionObserver.addListener(object : SessionListener { + override suspend fun onSessionCreated(userId: String) = Unit + + override suspend fun onSessionDeleted(userId: String) { + map.remove(SessionId(userId)) + } + }) + } + + override fun get(client: MatrixClient): ImageLoader { + return synchronized(map) { + map.getOrPut(client.sessionId) { + LoggedInImageLoaderFactory( + context = context, + matrixClient = client, + okHttpClient = okHttpClient, + ).newImageLoader() + } + } + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/toHtmlDocument.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/ToHtmlDocument.kt similarity index 52% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/toHtmlDocument.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/ToHtmlDocument.kt index a38b631eb2..8db1e6b5db 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/toHtmlDocument.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/ToHtmlDocument.kt @@ -14,19 +14,46 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.timeline.util +package io.element.android.libraries.matrix.ui.messages +import io.element.android.libraries.matrix.api.permalink.PermalinkData +import io.element.android.libraries.matrix.api.permalink.PermalinkParser import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat import org.jsoup.Jsoup import org.jsoup.nodes.Document +/** + * Converts the HTML string [FormattedBody.body] to a [Document] by parsing it. + * If the message is not formatted or the format is not [MessageFormat.HTML] we return `null`. + * + * This will also make sure mentions are prefixed with `@`. + * + * @param prefix if not null, the prefix will be inserted at the beginning of the message. + */ fun FormattedBody.toHtmlDocument(prefix: String? = null): Document? { return takeIf { it.format == MessageFormat.HTML }?.body?.let { formattedBody -> - if (prefix != null) { + val dom = if (prefix != null) { Jsoup.parse("$prefix $formattedBody") } else { Jsoup.parse(formattedBody) } + + // Prepend `@` to mentions + fixMentions(dom) + + dom + } +} + +private fun fixMentions(dom: Document) { + val links = dom.getElementsByTag("a") + links.forEach { + if (it.hasAttr("href")) { + val link = PermalinkParser.parse(it.attr("href")) + if (link is PermalinkData.UserLink && !it.text().startsWith("@")) { + it.prependText("@") + } + } } } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/ToPlainText.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/ToPlainText.kt new file mode 100644 index 0000000000..f20252c42a --- /dev/null +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/ToPlainText.kt @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrix.ui.messages + +import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody +import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import org.jsoup.nodes.Node +import org.jsoup.nodes.TextNode +import org.jsoup.select.NodeVisitor + +/** + * Converts the HTML string in [TextMessageType.formatted] to a plain text representation by parsing it and removing all formatting. + * If the message is not formatted or the format is not [MessageFormat.HTML], the [TextMessageType.body] is returned instead. + */ +fun TextMessageType.toPlainText() = formatted?.toPlainText() ?: body + +/** + * Converts the HTML string in [FormattedBody.body] to a plain text representation by parsing it and removing all formatting. + * If the message is not formatted or the format is not [MessageFormat.HTML] we return `null`. + * @param prefix if not null, the prefix will be inserted at the beginning of the message. + */ +fun FormattedBody.toPlainText(prefix: String? = null): String? { + return this.toHtmlDocument(prefix)?.toPlainText() +} + +/** + * Converts the HTML [Document] to a plain text representation by parsing it and removing all formatting. + */ +fun Document.toPlainText(): String { + val visitor = PlainTextNodeVisitor() + traverse(visitor) + return visitor.build() +} + +private class PlainTextNodeVisitor : NodeVisitor { + private val builder = StringBuilder() + + override fun head(node: Node, depth: Int) { + if (node is TextNode && node.text().isNotBlank()) { + builder.append(node.text()) + } else if (node is Element && node.tagName() == "li") { + val index = node.elementSiblingIndex() + val isOrdered = node.parent()?.nodeName()?.lowercase() == "ol" + if (isOrdered) { + builder.append("${index + 1}. ") + } else { + builder.append("• ") + } + } else if (node is Element && node.isBlock && builder.lastOrNull() != '\n') { + builder.append("\n") + } + } + + override fun tail(node: Node, depth: Int) { + fun nodeIsBlockButNotLastOne(node: Node) = node is Element && node.isBlock && node.lastElementSibling() !== node + fun nodeIsLineBreak(node: Node) = node.nodeName().lowercase() == "br" + if (nodeIsBlockButNotLastOne(node) || nodeIsLineBreak(node)) { + builder.append("\n") + } + } + + fun build(): String { + return builder.toString().trim() + } +} diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/theme/Theme.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/PollHistory.kt similarity index 53% rename from libraries/theme/src/main/kotlin/io/element/android/libraries/theme/theme/Theme.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/PollHistory.kt index 2d77a66c98..bc695864f7 100644 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/theme/Theme.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/PollHistory.kt @@ -14,33 +14,23 @@ * limitations under the License. */ -package io.element.android.libraries.theme.theme +package io.element.android.libraries.matrix.ui.room -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.Composable -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map - -enum class Theme { - System, - Dark, - Light; -} - -val themes = listOf(Theme.System, Theme.Dark, Theme.Light) +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.remember +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.timeline.MatrixTimeline @Composable -fun Theme.isDark(): Boolean { - return when (this) { - Theme.System -> isSystemInDarkTheme() - Theme.Dark -> true - Theme.Light -> false +fun MatrixRoom.rememberPollHistory(): MatrixTimeline { + val pollHistory = remember { + pollHistory() } -} - -fun Flow.mapToTheme(): Flow = map { - when (it) { - null -> Theme.System - else -> Theme.valueOf(it) + DisposableEffect(pollHistory) { + onDispose { + pollHistory.close() + } } + return pollHistory } diff --git a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrixui/messages/ToHtmlDocumentTest.kt b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrixui/messages/ToHtmlDocumentTest.kt new file mode 100644 index 0000000000..d6c740b77d --- /dev/null +++ b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrixui/messages/ToHtmlDocumentTest.kt @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrixui.messages + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody +import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat +import io.element.android.libraries.matrix.ui.messages.toHtmlDocument +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class ToHtmlDocumentTest { + + @Test + fun `toHtmlDocument - returns null if format is not HTML`() { + val body = FormattedBody( + format = MessageFormat.UNKNOWN, + body = "Hello world" + ) + + val document = body.toHtmlDocument() + + assertThat(document).isNull() + } + + @Test + fun `toHtmlDocument - returns a Document if the format is HTML`() { + val body = FormattedBody( + format = MessageFormat.HTML, + body = "

Hello world

" + ) + + val document = body.toHtmlDocument() + assertThat(document).isNotNull() + assertThat(document?.text()).isEqualTo("Hello world") + } + + @Test + fun `toHtmlDocument - returns a Document with a prefix if provided`() { + val body = FormattedBody( + format = MessageFormat.HTML, + body = "

Hello world

" + ) + + val document = body.toHtmlDocument(prefix = "@Jorge:") + assertThat(document).isNotNull() + assertThat(document?.text()).isEqualTo("@Jorge: Hello world") + } + + @Test + fun `toHtmlDocument - if a mention is found without an '@' prefix, it will be added`() { + val body = FormattedBody( + format = MessageFormat.HTML, + body = "Hey Alice!" + ) + + val document = body.toHtmlDocument() + assertThat(document?.text()).isEqualTo("Hey @Alice!") + } + + @Test + fun `toHtmlDocument - if a mention is found with an '@' prefix, nothing will be done`() { + val body = FormattedBody( + format = MessageFormat.HTML, + body = "Hey @Alice!" + ) + + val document = body.toHtmlDocument() + assertThat(document?.text()).isEqualTo("Hey @Alice!") + } + + @Test + fun `toHtmlDocument - if a link is not a mention, nothing will be done for it`() { + val body = FormattedBody( + format = MessageFormat.HTML, + body = "Hey Alice!" + ) + + val document = body.toHtmlDocument() + assertThat(document?.text()).isEqualTo("Hey Alice!") + } +} diff --git a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrixui/messages/ToPlainTextTest.kt b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrixui/messages/ToPlainTextTest.kt new file mode 100644 index 0000000000..3c1a469f42 --- /dev/null +++ b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrixui/messages/ToPlainTextTest.kt @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrixui.messages + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody +import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat +import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType +import io.element.android.libraries.matrix.ui.messages.toPlainText +import org.jsoup.Jsoup +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class ToPlainTextTest { + + @Test + fun `Document toPlainText - returns a plain text version of the document`() { + val document = Jsoup.parse( + """ + Hello world +
  • This is an unordered list.
+
  1. This is an ordered list.
+
+ """.trimIndent() + ) + + assertThat(document.toPlainText()).isEqualTo(""" + Hello world + • This is an unordered list. + 1. This is an ordered list. + """.trimIndent() + ) + } + + @Test + fun `FormattedBody toPlainText - returns a plain text version of the HTML body`() { + val formattedBody = FormattedBody( + format = MessageFormat.HTML, + body = """ + Hello world +
  • This is an unordered list.
+
  1. This is an ordered list.
+
+ """.trimIndent() + ) + assertThat(formattedBody.toPlainText()).isEqualTo(""" + Hello world + • This is an unordered list. + 1. This is an ordered list. + """.trimIndent() + ) + } + + @Test + fun `FormattedBody toPlainText - returns null if the format is not HTML`() { + val formattedBody = FormattedBody( + format = MessageFormat.UNKNOWN, + body = """ + Hello world +
  • This is an unordered list.
+
  1. This is an ordered list.
+
+ """.trimIndent() + ) + assertThat(formattedBody.toPlainText()).isNull() + } + + @Test + fun `TextMessageType toPlainText - returns a plain text version of the HTML body`() { + val messageType = TextMessageType( + body = "Hello world\n- This in an unordered list.\n1. This is an ordered list.\n", + formatted = FormattedBody( + format = MessageFormat.HTML, + body = """ + Hello world +
  • This is an unordered list.
+
  1. This is an ordered list.
+
+ """.trimIndent() + ) + ) + assertThat(messageType.toPlainText()).isEqualTo(""" + Hello world + • This is an unordered list. + 1. This is an ordered list. + """.trimIndent() + ) + } + + @Test + fun `TextMessageType toPlainText - returns the markdown body if the formatted one cannot be parsed`() { + val messageType = TextMessageType( + body = "This is the fallback text", + formatted = FormattedBody( + format = MessageFormat.UNKNOWN, + body = """ + Hello world +
  • This is an unordered list.
+
  1. This is an ordered list.
+
+ """.trimIndent() + ) + ) + assertThat(messageType.toPlainText()).isEqualTo("This is the fallback text") + } +} diff --git a/libraries/mediapickers/impl/src/main/kotlin/io/element/android/libraries/mediapickers/impl/PickerProviderImpl.kt b/libraries/mediapickers/impl/src/main/kotlin/io/element/android/libraries/mediapickers/impl/PickerProviderImpl.kt index 201fd9b069..f3c186a34f 100644 --- a/libraries/mediapickers/impl/src/main/kotlin/io/element/android/libraries/mediapickers/impl/PickerProviderImpl.kt +++ b/libraries/mediapickers/impl/src/main/kotlin/io/element/android/libraries/mediapickers/impl/PickerProviderImpl.kt @@ -37,7 +37,7 @@ import java.util.UUID import javax.inject.Inject @ContributesBinding(AppScope::class) -class PickerProviderImpl constructor(private val isInTest: Boolean) : PickerProvider { +class PickerProviderImpl(private val isInTest: Boolean) : PickerProvider { @Inject constructor(): this(false) diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaPreProcessor.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaPreProcessor.kt index 31c6a813ff..9a48b50aca 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaPreProcessor.kt +++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaPreProcessor.kt @@ -31,6 +31,6 @@ interface MediaPreProcessor { compressIfPossible: Boolean ): Result - data class Failure(override val cause: Throwable?) : RuntimeException(cause) + data class Failure(override val cause: Throwable?) : Exception(cause) } diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt index dd51198e59..dfcef7fb33 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt +++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt @@ -101,7 +101,6 @@ class MediaSender @Inject constructor( progressCallback = progressCallback ) } - is MediaUploadInfo.Video -> { sendVideo( file = uploadInfo.file, diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaUploadInfo.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaUploadInfo.kt index 39c978f625..76e6fd7cb1 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaUploadInfo.kt +++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaUploadInfo.kt @@ -26,8 +26,8 @@ sealed interface MediaUploadInfo { val file: File - data class Image(override val file: File, val imageInfo: ImageInfo, val thumbnailFile: File) : MediaUploadInfo - data class Video(override val file: File, val videoInfo: VideoInfo, val thumbnailFile: File) : MediaUploadInfo + data class Image(override val file: File, val imageInfo: ImageInfo, val thumbnailFile: File?) : MediaUploadInfo + data class Video(override val file: File, val videoInfo: VideoInfo, val thumbnailFile: File?) : MediaUploadInfo data class Audio(override val file: File, val audioInfo: AudioInfo) : MediaUploadInfo data class VoiceMessage(override val file: File, val audioInfo: AudioInfo, val waveform: List) : MediaUploadInfo data class AnyFile(override val file: File, val fileInfo: FileInfo) : MediaUploadInfo diff --git a/libraries/mediaupload/api/src/test/kotlin/io/element/android/libraries/mediaupload/api/MediaSenderTests.kt b/libraries/mediaupload/api/src/test/kotlin/io/element/android/libraries/mediaupload/api/MediaSenderTests.kt index 480cf8065f..35aee8b2d5 100644 --- a/libraries/mediaupload/api/src/test/kotlin/io/element/android/libraries/mediaupload/api/MediaSenderTests.kt +++ b/libraries/mediaupload/api/src/test/kotlin/io/element/android/libraries/mediaupload/api/MediaSenderTests.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.mediaupload.api import android.net.Uri import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor @@ -39,7 +40,7 @@ class MediaSenderTests { val sender = aMediaSender(preProcessor) val uri = Uri.parse("content://image.jpg") - sender.sendMedia(uri = uri, mimeType = "image/jpeg", compressIfPossible = true) + sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg, compressIfPossible = true) assertThat(preProcessor.processCallCount).isEqualTo(1) } @@ -50,7 +51,7 @@ class MediaSenderTests { val sender = aMediaSender(room = room) val uri = Uri.parse("content://image.jpg") - sender.sendMedia(uri = uri, mimeType = "image/jpeg", compressIfPossible = true) + sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg, compressIfPossible = true) assertThat(room.sendMediaCount).isEqualTo(1) } @@ -63,7 +64,7 @@ class MediaSenderTests { val sender = aMediaSender(preProcessor) val uri = Uri.parse("content://image.jpg") - val result = sender.sendMedia(uri = uri, mimeType = "image/jpeg", compressIfPossible = true) + val result = sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg, compressIfPossible = true) assertThat(result.exceptionOrNull()).isNotNull() } @@ -76,7 +77,7 @@ class MediaSenderTests { val sender = aMediaSender(room = room) val uri = Uri.parse("content://image.jpg") - val result = sender.sendMedia(uri = uri, mimeType = "image/jpeg", compressIfPossible = true) + val result = sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg, compressIfPossible = true) assertThat(result.exceptionOrNull()).isNotNull() } @@ -88,7 +89,7 @@ class MediaSenderTests { val sender = aMediaSender(room = room) val sendJob = launch { val uri = Uri.parse("content://image.jpg") - sender.sendMedia(uri = uri, mimeType = "image/jpeg", compressIfPossible = true) + sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg, compressIfPossible = true) } // Wait until several internal tasks run and the file is being uploaded advanceTimeBy(3L) diff --git a/libraries/mediaupload/impl/build.gradle.kts b/libraries/mediaupload/impl/build.gradle.kts index a23ab14b74..c65b6b01a6 100644 --- a/libraries/mediaupload/impl/build.gradle.kts +++ b/libraries/mediaupload/impl/build.gradle.kts @@ -27,6 +27,12 @@ android { generateDaggerFactories.set(true) } + testOptions { + unitTests { + isIncludeAndroidResources = true + } + } + dependencies { implementation(projects.anvilannotations) anvil(projects.anvilcodegen) @@ -37,6 +43,7 @@ android { implementation(projects.libraries.core) implementation(projects.libraries.di) implementation(projects.libraries.matrix.api) + implementation(projects.services.toolbox.api) implementation(libs.inject) implementation(libs.androidx.exifinterface) implementation(libs.coroutines.core) @@ -44,7 +51,10 @@ android { implementation(libs.vanniktech.blurhash) testImplementation(libs.test.junit) + testImplementation(libs.test.robolectric) testImplementation(libs.coroutines.test) testImplementation(libs.test.truth) + testImplementation(projects.tests.testutils) + testImplementation(projects.services.toolbox.test) } } diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/AndroidMediaPreProcessor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt similarity index 91% rename from libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/AndroidMediaPreProcessor.kt rename to libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt index 205be3b241..486364a7d7 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/AndroidMediaPreProcessor.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.mediaupload +package io.element.android.libraries.mediaupload.impl import android.content.Context import android.graphics.BitmapFactory @@ -47,8 +47,9 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.withContext import java.io.File import java.io.InputStream -import java.time.Duration import javax.inject.Inject +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds @ContributesBinding(AppScope::class) class AndroidMediaPreProcessor @Inject constructor( @@ -136,7 +137,7 @@ class AndroidMediaPreProcessor @Inject constructor( resizeMode = ResizeMode.Approximate(IMAGE_SCALE_REF_SIZE, IMAGE_SCALE_REF_SIZE), orientation = orientation, ).getOrThrow() - val thumbnailResult: ThumbnailResult = thumbnailFactory.createImageThumbnail(compressionResult.file) + val thumbnailResult = thumbnailFactory.createImageThumbnail(compressionResult.file) val imageInfo = compressionResult.toImageInfo( mimeType = mimeType, thumbnailResult = thumbnailResult @@ -145,13 +146,13 @@ class AndroidMediaPreProcessor @Inject constructor( return MediaUploadInfo.Image( file = compressionResult.file, imageInfo = imageInfo, - thumbnailFile = thumbnailResult.file + thumbnailFile = thumbnailResult?.file ) } suspend fun processImageWithoutCompression(): MediaUploadInfo { val file = copyToTmpFile(uri) - val thumbnailResult: ThumbnailResult = thumbnailFactory.createImageThumbnail(file) + val thumbnailResult = thumbnailFactory.createImageThumbnail(file) val imageInfo = contentResolver.openInputStream(uri).use { input -> val bitmap = BitmapFactory.decodeStream(input, null, null)!! ImageInfo( @@ -159,16 +160,16 @@ class AndroidMediaPreProcessor @Inject constructor( height = bitmap.height.toLong(), mimetype = mimeType, size = file.length(), - thumbnailInfo = thumbnailResult.info, + thumbnailInfo = thumbnailResult?.info, thumbnailSource = null, - blurhash = thumbnailResult.blurhash, + blurhash = thumbnailResult?.blurhash, ) } removeSensitiveImageMetadata(file) return MediaUploadInfo.Image( file = file, imageInfo = imageInfo, - thumbnailFile = thumbnailResult.file + thumbnailFile = thumbnailResult?.file ) } @@ -196,7 +197,7 @@ class AndroidMediaPreProcessor @Inject constructor( return MediaUploadInfo.Video( file = resultFile, videoInfo = videoInfo, - thumbnailFile = thumbnailInfo.file + thumbnailFile = thumbnailInfo?.file ) } @@ -234,7 +235,7 @@ class AndroidMediaPreProcessor @Inject constructor( } } - private fun extractVideoMetadata(file: File, mimeType: String?, thumbnailResult: ThumbnailResult): VideoInfo = + private fun extractVideoMetadata(file: File, mimeType: String?, thumbnailResult: ThumbnailResult?): VideoInfo = MediaMetadataRetriever().runAndRelease { setDataSource(context, Uri.fromFile(file)) VideoInfo( @@ -243,10 +244,10 @@ class AndroidMediaPreProcessor @Inject constructor( height = extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)?.toLong() ?: 0L, mimetype = mimeType, size = file.length(), - thumbnailInfo = thumbnailResult.info, + thumbnailInfo = thumbnailResult?.info, // Will be computed by the rust sdk thumbnailSource = null, - blurhash = thumbnailResult.blurhash, + blurhash = thumbnailResult?.blurhash, ) } @@ -256,19 +257,19 @@ class AndroidMediaPreProcessor @Inject constructor( } } -fun ImageCompressionResult.toImageInfo(mimeType: String, thumbnailResult: ThumbnailResult) = ImageInfo( +private fun ImageCompressionResult.toImageInfo(mimeType: String, thumbnailResult: ThumbnailResult?) = ImageInfo( width = width.toLong(), height = height.toLong(), mimetype = mimeType, size = size, - thumbnailInfo = thumbnailResult.info, + thumbnailInfo = thumbnailResult?.info, // Will be computed by the rust sdk thumbnailSource = null, - blurhash = thumbnailResult.blurhash, + blurhash = thumbnailResult?.blurhash, ) private fun MediaMetadataRetriever.extractDuration(): Duration { val durationInMs = extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L - return Duration.ofMillis(durationInMs) + return durationInMs.milliseconds } diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ImageCompressor.kt similarity index 95% rename from libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt rename to libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ImageCompressor.kt index d936b3a5bf..4378889f97 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ImageCompressor.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.mediaupload +package io.element.android.libraries.mediaupload.impl import android.content.Context import android.graphics.Bitmap @@ -24,8 +24,8 @@ import io.element.android.libraries.androidutils.bitmap.calculateInSampleSize import io.element.android.libraries.androidutils.bitmap.resizeToMax import io.element.android.libraries.androidutils.bitmap.rotateToMetadataOrientation import io.element.android.libraries.androidutils.file.createTmpFile +import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.di.ApplicationContext -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.io.File import java.io.InputStream @@ -33,8 +33,8 @@ import javax.inject.Inject class ImageCompressor @Inject constructor( @ApplicationContext private val context: Context, + private val dispatchers: CoroutineDispatchers, ) { - /** * Decodes the [inputStream] into a [Bitmap] and applies the needed transformations (rotation, scale) based on [resizeMode], then writes it into a * temporary file using the passed [format], [orientation] and [desiredQuality]. @@ -46,7 +46,7 @@ class ImageCompressor @Inject constructor( format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG, orientation: Int = ExifInterface.ORIENTATION_UNDEFINED, desiredQuality: Int = 80, - ): Result = withContext(Dispatchers.IO) { + ): Result = withContext(dispatchers.io) { runCatching { val compressedBitmap = compressToBitmap(inputStreamProvider, resizeMode, orientation).getOrThrow() // Encode bitmap to the destination temporary file diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ThumbnailFactory.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ThumbnailFactory.kt similarity index 64% rename from libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ThumbnailFactory.kt rename to libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ThumbnailFactory.kt index 2cee2566d1..1ee7b40bb3 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ThumbnailFactory.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ThumbnailFactory.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.mediaupload +package io.element.android.libraries.mediaupload.impl import android.annotation.SuppressLint import android.content.Context @@ -32,8 +32,11 @@ import io.element.android.libraries.androidutils.media.runAndRelease import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.matrix.api.media.ThumbnailInfo +import io.element.android.services.toolbox.api.sdk.BuildVersionSdkIntProvider import kotlinx.coroutines.suspendCancellableCoroutine +import timber.log.Timber import java.io.File +import java.io.IOException import javax.inject.Inject import kotlin.coroutines.resume @@ -56,29 +59,40 @@ private const val VIDEO_THUMB_FRAME = 0L class ThumbnailFactory @Inject constructor( @ApplicationContext private val context: Context, + private val sdkIntProvider: BuildVersionSdkIntProvider ) { @SuppressLint("NewApi") - suspend fun createImageThumbnail(file: File): ThumbnailResult { + suspend fun createImageThumbnail(file: File): ThumbnailResult? { return createThumbnail { cancellationSignal -> - // This API works correctly with GIF - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - ThumbnailUtils.createImageThumbnail( - file, - Size(THUMB_MAX_WIDTH, THUMB_MAX_HEIGHT), - cancellationSignal - ) - } else { - @Suppress("DEPRECATION") - ThumbnailUtils.createImageThumbnail( - file.path, - MediaStore.Images.Thumbnails.MINI_KIND, - ) + try { + // This API works correctly with GIF + if (sdkIntProvider.isAtLeast(Build.VERSION_CODES.Q)) { + try { + ThumbnailUtils.createImageThumbnail( + file, + Size(THUMB_MAX_WIDTH, THUMB_MAX_HEIGHT), + cancellationSignal + ) + } catch (ioException: IOException) { + Timber.w(ioException, "Failed to create thumbnail for $file") + null + } + } else { + @Suppress("DEPRECATION") + ThumbnailUtils.createImageThumbnail( + file.path, + MediaStore.Images.Thumbnails.MINI_KIND, + ) + } + } catch (throwable: Throwable) { + Timber.w(throwable, "Failed to create thumbnail for $file") + null } } } - suspend fun createVideoThumbnail(file: File): ThumbnailResult { + suspend fun createVideoThumbnail(file: File): ThumbnailResult? { return createThumbnail { MediaMetadataRetriever().runAndRelease { setDataSource(context, file.toUri()) @@ -87,37 +101,38 @@ class ThumbnailFactory @Inject constructor( } } - private suspend fun createThumbnail(bitmapFactory: (CancellationSignal) -> Bitmap?): ThumbnailResult = suspendCancellableCoroutine { continuation -> + private suspend fun createThumbnail(bitmapFactory: (CancellationSignal) -> Bitmap?): ThumbnailResult? = suspendCancellableCoroutine { continuation -> val cancellationSignal = CancellationSignal() continuation.invokeOnCancellation { cancellationSignal.cancel() } val bitmapThumbnail: Bitmap? = bitmapFactory(cancellationSignal) + if (bitmapThumbnail == null) { + continuation.resume(null) + return@suspendCancellableCoroutine + } val thumbnailFile = context.createTmpFile(extension = "jpeg") thumbnailFile.outputStream().use { outputStream -> - bitmapThumbnail?.compress(Bitmap.CompressFormat.JPEG, 80, outputStream) - } - val blurhash = bitmapThumbnail?.let { - BlurHash.encode(it, 3, 3) + bitmapThumbnail.compress(Bitmap.CompressFormat.JPEG, 80, outputStream) } + val blurhash = BlurHash.encode(bitmapThumbnail, 3, 3) val thumbnailResult = ThumbnailResult( file = thumbnailFile, info = ThumbnailInfo( - height = bitmapThumbnail?.height?.toLong(), - width = bitmapThumbnail?.width?.toLong(), + height = bitmapThumbnail.height.toLong(), + width = bitmapThumbnail.width.toLong(), mimetype = MimeTypes.Jpeg, size = thumbnailFile.length() ), blurhash = blurhash ) - bitmapThumbnail?.recycle() + bitmapThumbnail.recycle() continuation.resume(thumbnailResult) - } } data class ThumbnailResult( val file: File, val info: ThumbnailInfo, - val blurhash: String?, + val blurhash: String, ) diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/VideoCompressor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/VideoCompressor.kt similarity index 98% rename from libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/VideoCompressor.kt rename to libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/VideoCompressor.kt index 5587eedfa4..762dd04901 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/VideoCompressor.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/VideoCompressor.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.mediaupload +package io.element.android.libraries.mediaupload.impl import android.content.Context import android.net.Uri diff --git a/libraries/mediaupload/impl/src/test/assets/animated_gif.gif b/libraries/mediaupload/impl/src/test/assets/animated_gif.gif new file mode 100644 index 0000000000..8fd2ce1550 --- /dev/null +++ b/libraries/mediaupload/impl/src/test/assets/animated_gif.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6831610b21668c49e31732f9005177e959277233d3cab758910e061294f91d79 +size 687979 diff --git a/libraries/mediaupload/impl/src/test/assets/image.png b/libraries/mediaupload/impl/src/test/assets/image.png new file mode 100644 index 0000000000..b0946f7ba8 --- /dev/null +++ b/libraries/mediaupload/impl/src/test/assets/image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a980f7b74cb9edc323919db8652798da4b3dcf865fc7b6a1eb1110096b7bfb4f +size 1856786 diff --git a/libraries/mediaupload/impl/src/test/assets/sample3s.mp3 b/libraries/mediaupload/impl/src/test/assets/sample3s.mp3 new file mode 100644 index 0000000000..cb4db9c9d8 --- /dev/null +++ b/libraries/mediaupload/impl/src/test/assets/sample3s.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0244590f2b4bcb62352b574e78bea940e8d89cfa69823b5208ef4c43e0abcb44 +size 52079 diff --git a/libraries/mediaupload/impl/src/test/assets/text.txt b/libraries/mediaupload/impl/src/test/assets/text.txt new file mode 100644 index 0000000000..d45ec4338b --- /dev/null +++ b/libraries/mediaupload/impl/src/test/assets/text.txt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0ba904eae8773b70c75333db4de2f3ac45a8ad4ddba1b242f0b3cfc199391dd8 +size 13 diff --git a/libraries/mediaupload/impl/src/test/assets/video.mp4 b/libraries/mediaupload/impl/src/test/assets/video.mp4 new file mode 100644 index 0000000000..4d57318b1d --- /dev/null +++ b/libraries/mediaupload/impl/src/test/assets/video.mp4 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fb58436524db95bd0c10b2c3023c2eb7b87404a2eab8987939f051647eb859d3 +size 1673712 diff --git a/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt b/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt new file mode 100644 index 0000000000..4086627561 --- /dev/null +++ b/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.mediaupload.impl + +import android.content.Context +import android.os.Build +import androidx.core.net.toUri +import androidx.test.platform.app.InstrumentationRegistry +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.matrix.api.media.AudioInfo +import io.element.android.libraries.matrix.api.media.FileInfo +import io.element.android.libraries.matrix.api.media.ImageInfo +import io.element.android.libraries.matrix.api.media.ThumbnailInfo +import io.element.android.libraries.matrix.api.media.VideoInfo +import io.element.android.libraries.mediaupload.api.MediaPreProcessor +import io.element.android.libraries.mediaupload.api.MediaUploadInfo +import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvider +import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import java.io.File +import java.io.FileNotFoundException +import java.io.IOException +import kotlin.time.Duration + +@RunWith(RobolectricTestRunner::class) +class AndroidMediaPreProcessorTest { + @Test + fun `test processing image`() = runTest { + val context = InstrumentationRegistry.getInstrumentation().context + val sut = createAndroidMediaPreProcessor(context) + val file = getFileFromAssets(context, "image.png") + val result = sut.process( + uri = file.toUri(), + mimeType = MimeTypes.Png, + deleteOriginal = false, + compressIfPossible = true, + ) + val data = result.getOrThrow() + assertThat(data.file.path).endsWith("image.png") + val info = data as MediaUploadInfo.Image + // Computing thumbnailFile is failing with Robolectric + assertThat(info.thumbnailFile).isNull() + assertThat(info.imageInfo).isEqualTo( + ImageInfo( + height = 1_178, + width = 1_818, + mimetype = MimeTypes.Png, + size = 114_867, + thumbnailInfo = null, + thumbnailSource = null, + blurhash = null, + ) + ) + assertThat(file.exists()).isTrue() + } + + @Test + fun `test processing image api Q`() = runTest { + val context = InstrumentationRegistry.getInstrumentation().context + val sut = createAndroidMediaPreProcessor(context, sdkIntVersion = Build.VERSION_CODES.Q) + val file = getFileFromAssets(context, "image.png") + val result = sut.process( + uri = file.toUri(), + mimeType = MimeTypes.Png, + deleteOriginal = false, + compressIfPossible = true, + ) + val data = result.getOrThrow() + assertThat(data.file.path).endsWith("image.png") + val info = data as MediaUploadInfo.Image + // Computing thumbnailFile is failing with Robolectric + assertThat(info.thumbnailFile).isNull() + assertThat(info.imageInfo).isEqualTo( + ImageInfo( + height = 1_178, + width = 1_818, + mimetype = MimeTypes.Png, + size = 114_867, + thumbnailInfo = null, + thumbnailSource = null, + blurhash = null, + ) + ) + assertThat(file.exists()).isTrue() + } + + @Test + fun `test processing image no compression`() = runTest { + val context = InstrumentationRegistry.getInstrumentation().context + val sut = createAndroidMediaPreProcessor(context) + val file = getFileFromAssets(context, "image.png") + val result = sut.process( + uri = file.toUri(), + mimeType = MimeTypes.Png, + deleteOriginal = false, + compressIfPossible = false, + ).getOrThrow() + assertThat(result.file.path).endsWith("image.png") + val info = result as MediaUploadInfo.Image + assertThat(info.thumbnailFile).isNotNull() + assertThat(info.imageInfo).isEqualTo( + ImageInfo( + height = 1_178, + width = 1_818, + mimetype = MimeTypes.Png, + size = 1_856_786, + thumbnailInfo = ThumbnailInfo(height = 25, width = 25, mimetype = MimeTypes.Jpeg, size = 643), + thumbnailSource = null, + blurhash = "K00000fQfQfQfQfQfQfQfQ", + ) + ) + assertThat(file.exists()).isTrue() + } + + @Test + fun `test processing image and delete`() = runTest { + val context = InstrumentationRegistry.getInstrumentation().context + val sut = createAndroidMediaPreProcessor(context) + val file = getFileFromAssets(context, "image.png") + val result = sut.process( + uri = file.toUri(), + mimeType = MimeTypes.Png, + deleteOriginal = true, + compressIfPossible = false, + ).getOrThrow() + assertThat(result.file.path).endsWith("image.png") + val info = result as MediaUploadInfo.Image + assertThat(info.thumbnailFile).isNotNull() + assertThat(info.imageInfo).isEqualTo( + ImageInfo( + height = 1_178, + width = 1_818, + mimetype = MimeTypes.Png, + size = 1_856_786, + thumbnailInfo = ThumbnailInfo(height = 25, width = 25, mimetype = MimeTypes.Jpeg, size = 643), + thumbnailSource = null, + blurhash = "K00000fQfQfQfQfQfQfQfQ", + ) + ) + // Does not work + // assertThat(file.exists()).isFalse() + } + + @Test + fun `test processing gif`() = runTest { + val context = InstrumentationRegistry.getInstrumentation().context + val sut = createAndroidMediaPreProcessor(context) + val file = getFileFromAssets(context, "animated_gif.gif") + val result = sut.process( + uri = file.toUri(), + mimeType = MimeTypes.Gif, + deleteOriginal = false, + compressIfPossible = true, + ).getOrThrow() + assertThat(result.file.path).endsWith("animated_gif.gif") + val info = result as MediaUploadInfo.Image + assertThat(info.thumbnailFile).isNotNull() + assertThat(info.imageInfo).isEqualTo( + ImageInfo( + height = 600, + width = 800, + mimetype = MimeTypes.Gif, + size = 687_979, + thumbnailInfo = ThumbnailInfo(height = 50, width = 50, mimetype = MimeTypes.Jpeg, size = 691), + thumbnailSource = null, + blurhash = "K00000fQfQfQfQfQfQfQfQ", + ) + ) + assertThat(file.exists()).isTrue() + } + + @Test + fun `test processing file`() = runTest { + val context = InstrumentationRegistry.getInstrumentation().context + val sut = createAndroidMediaPreProcessor(context) + val file = getFileFromAssets(context, "text.txt") + val result = sut.process( + uri = file.toUri(), + mimeType = MimeTypes.PlainText, + deleteOriginal = false, + compressIfPossible = true, + ).getOrThrow() + assertThat(result.file.path).endsWith("text.txt") + val info = result as MediaUploadInfo.AnyFile + assertThat(info.fileInfo).isEqualTo( + FileInfo( + mimetype = MimeTypes.PlainText, + size = 13, + thumbnailInfo = null, + thumbnailSource = null, + ) + ) + assertThat(file.exists()).isTrue() + } + + @Ignore("Compressing video is not working with Robolectric") + @Test + fun `test processing video`() = runTest { + val context = InstrumentationRegistry.getInstrumentation().context + val sut = createAndroidMediaPreProcessor(context) + val file = getFileFromAssets(context, "video.mp4") + val result = sut.process( + uri = file.toUri(), + mimeType = MimeTypes.Mp4, + deleteOriginal = false, + compressIfPossible = true, + ).getOrThrow() + assertThat(result.file.path).endsWith("video.mp4") + val info = result as MediaUploadInfo.Video + assertThat(info.thumbnailFile).isNotNull() + assertThat(info.videoInfo).isEqualTo( + VideoInfo( + duration = Duration.ZERO, // Not available with Robolectric? + height = 1_178, + width = 1_818, + mimetype = MimeTypes.Mp4, + size = 114_867, + thumbnailInfo = null, + thumbnailSource = null, + blurhash = null, + ) + ) + assertThat(file.exists()).isTrue() + } + + @Test + fun `test processing video no compression`() = runTest { + val context = InstrumentationRegistry.getInstrumentation().context + val sut = createAndroidMediaPreProcessor(context) + val file = getFileFromAssets(context, "video.mp4") + val result = sut.process( + uri = file.toUri(), + mimeType = MimeTypes.Mp4, + deleteOriginal = false, + compressIfPossible = false, + ).getOrThrow() + assertThat(result.file.path).endsWith("video.mp4") + val info = result as MediaUploadInfo.Video + // Computing thumbnailFile is failing with Robolectric + assertThat(info.thumbnailFile).isNull() + assertThat(info.videoInfo).isEqualTo( + VideoInfo( + duration = Duration.ZERO, // Not available with Robolectric? + height = 0, // Not available with Robolectric? + width = 0, // Not available with Robolectric? + mimetype = MimeTypes.Mp4, + size = 1_673_712, + thumbnailInfo = null, + thumbnailSource = null, + blurhash = null, + ) + ) + assertThat(file.exists()).isTrue() + } + + @Test + fun `test processing audio`() = runTest { + val context = InstrumentationRegistry.getInstrumentation().context + val sut = createAndroidMediaPreProcessor(context) + val file = getFileFromAssets(context, "sample3s.mp3") + val result = sut.process( + uri = file.toUri(), + mimeType = MimeTypes.Mp3, + deleteOriginal = false, + compressIfPossible = true, + ).getOrThrow() + assertThat(result.file.path).endsWith("sample3s.mp3") + val info = result as MediaUploadInfo.Audio + assertThat(info.audioInfo).isEqualTo( + AudioInfo( + duration = Duration.ZERO, // Not available with Robolectric? + size = 52_079, + mimetype = MimeTypes.Mp3, + ) + ) + assertThat(file.exists()).isTrue() + } + + @Test + fun `test file which does not exist`() = runTest { + val context = InstrumentationRegistry.getInstrumentation().context + val sut = createAndroidMediaPreProcessor(context) + val file = File(context.cacheDir, "not found.txt") + val result = sut.process( + uri = file.toUri(), + mimeType = MimeTypes.PlainText, + deleteOriginal = false, + compressIfPossible = true, + ) + assertThat(result.isFailure).isTrue() + val failure = result.exceptionOrNull() + assertThat(failure).isInstanceOf(MediaPreProcessor.Failure::class.java) + assertThat(failure?.cause).isInstanceOf(FileNotFoundException::class.java) + } + + private fun TestScope.createAndroidMediaPreProcessor( + context: Context, + sdkIntVersion: Int = Build.VERSION_CODES.P + ) = AndroidMediaPreProcessor( + context = context, + thumbnailFactory = ThumbnailFactory(context, FakeBuildVersionSdkIntProvider(sdkIntVersion)), + imageCompressor = ImageCompressor(context, testCoroutineDispatchers()), + videoCompressor = VideoCompressor(context), + coroutineDispatchers = testCoroutineDispatchers(), + ) + + @Throws(IOException::class) + private fun getFileFromAssets(context: Context, fileName: String): File = File(context.cacheDir, fileName) + .also { + if (!it.exists()) { + it.outputStream().use { cache -> + context.assets.open(fileName).use { inputStream -> + inputStream.copyTo(cache) + } + } + } + } +} diff --git a/libraries/mediaupload/test/build.gradle.kts b/libraries/mediaupload/test/build.gradle.kts index 956afffbe0..a0d3ed35cc 100644 --- a/libraries/mediaupload/test/build.gradle.kts +++ b/libraries/mediaupload/test/build.gradle.kts @@ -24,5 +24,6 @@ android { dependencies { api(projects.libraries.mediaupload.api) + implementation(projects.libraries.core) implementation(projects.tests.testutils) } diff --git a/libraries/mediaupload/test/src/main/kotlin/io/element/android/libraries/mediaupload/test/FakeMediaPreProcessor.kt b/libraries/mediaupload/test/src/main/kotlin/io/element/android/libraries/mediaupload/test/FakeMediaPreProcessor.kt index 8e7e71e8fb..0c612c8d1c 100644 --- a/libraries/mediaupload/test/src/main/kotlin/io/element/android/libraries/mediaupload/test/FakeMediaPreProcessor.kt +++ b/libraries/mediaupload/test/src/main/kotlin/io/element/android/libraries/mediaupload/test/FakeMediaPreProcessor.kt @@ -17,6 +17,7 @@ package io.element.android.libraries.mediaupload.test import android.net.Uri +import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.media.FileInfo import io.element.android.libraries.mediaupload.api.MediaPreProcessor @@ -24,7 +25,6 @@ import io.element.android.libraries.mediaupload.api.MediaUploadInfo import io.element.android.tests.testutils.simulateLongTask import java.io.File import kotlin.time.Duration.Companion.seconds -import kotlin.time.toJavaDuration class FakeMediaPreProcessor : MediaPreProcessor { @@ -35,7 +35,7 @@ class FakeMediaPreProcessor : MediaPreProcessor { MediaUploadInfo.AnyFile( File("test"), FileInfo( - mimetype = "*/*", + mimetype = MimeTypes.Any, size = 999L, thumbnailInfo = null, thumbnailSource = null, @@ -63,9 +63,9 @@ class FakeMediaPreProcessor : MediaPreProcessor { MediaUploadInfo.Audio( file = File("audio.ogg"), audioInfo = AudioInfo( - duration = 1000.seconds.toJavaDuration(), + duration = 1000.seconds, size = 1000, - mimetype = "audio/ogg", + mimetype = MimeTypes.Ogg, ), ) ) diff --git a/libraries/mediaviewer/api/build.gradle.kts b/libraries/mediaviewer/api/build.gradle.kts new file mode 100644 index 0000000000..7076a144fe --- /dev/null +++ b/libraries/mediaviewer/api/build.gradle.kts @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 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. + */ +plugins { + id("io.element.android-compose-library") + alias(libs.plugins.anvil) + alias(libs.plugins.ksp) + id("kotlin-parcelize") +} + +android { + namespace = "io.element.android.libraries.mediaviewer.api" +} + +anvil { + generateDaggerFactories.set(true) +} + +dependencies { + anvil(projects.anvilcodegen) + implementation(projects.anvilannotations) + + implementation(libs.coil.compose) + implementation(libs.androidx.media3.exoplayer) + implementation(libs.androidx.media3.ui) + implementation(libs.coroutines.core) + implementation(libs.dagger) + implementation(libs.telephoto.zoomableimage) + implementation(libs.vanniktech.blurhash) + + implementation(projects.libraries.androidutils) + implementation(projects.libraries.architecture) + implementation(projects.libraries.core) + implementation(projects.libraries.di) + implementation(projects.libraries.designsystem) + implementation(projects.libraries.matrix.api) + implementation(projects.libraries.matrixui) + implementation(projects.libraries.uiStrings) + + testImplementation(projects.libraries.matrix.test) + testImplementation(projects.libraries.mediaviewer.test) + testImplementation(projects.tests.testutils) + testImplementation(libs.test.junit) + testImplementation(libs.test.truth) + testImplementation(libs.test.mockk) + testImplementation(libs.test.robolectric) + testImplementation(libs.test.turbine) + testImplementation(libs.coroutines.core) + testImplementation(libs.coroutines.test) + + ksp(libs.showkase.processor) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/helper/fileExtensionAndSize.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/helper/fileExtensionAndSize.kt similarity index 93% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/helper/fileExtensionAndSize.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/helper/fileExtensionAndSize.kt index 251d7a0f06..a2a4caec44 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/helper/fileExtensionAndSize.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/helper/fileExtensionAndSize.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.helper +package io.element.android.libraries.mediaviewer.api.helper fun formatFileExtensionAndSize(extension: String, size: String?): String { return buildString { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMedia.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMedia.kt similarity index 93% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMedia.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMedia.kt index 549842428a..067979dc93 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMedia.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMedia.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.local +package io.element.android.libraries.mediaviewer.api.local import android.net.Uri import android.os.Parcelable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaActions.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaActions.kt similarity index 95% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaActions.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaActions.kt index f35af36057..158c748a94 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaActions.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaActions.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.local +package io.element.android.libraries.mediaviewer.api.local import androidx.compose.runtime.Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaFactory.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaFactory.kt similarity index 95% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaFactory.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaFactory.kt index 36852a5a80..64dfbd03d8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaFactory.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaFactory.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.local +package io.element.android.libraries.mediaviewer.api.local import android.net.Uri import io.element.android.libraries.matrix.api.media.MediaFile diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaView.kt similarity index 93% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaView.kt index 96e721e815..1e81f75e9f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaView.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.local +package io.element.android.libraries.mediaviewer.api.local import android.annotation.SuppressLint import android.net.Uri @@ -44,6 +44,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -54,10 +55,10 @@ import androidx.media3.common.Player import androidx.media3.common.util.UnstableApi import androidx.media3.ui.AspectRatioFrameLayout import androidx.media3.ui.PlayerView -import io.element.android.features.messages.impl.media.helper.formatFileExtensionAndSize -import io.element.android.features.messages.impl.media.local.exoplayer.ExoPlayerWrapper -import io.element.android.features.messages.impl.media.local.pdf.PdfViewer -import io.element.android.features.messages.impl.media.local.pdf.rememberPdfViewerState +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.mediaviewer.api.local.exoplayer.ExoPlayerWrapper +import io.element.android.libraries.mediaviewer.api.local.pdf.PdfViewer +import io.element.android.libraries.mediaviewer.api.local.pdf.rememberPdfViewerState import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAudio @@ -68,7 +69,8 @@ import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.designsystem.utils.KeepScreenOn import io.element.android.libraries.designsystem.utils.OnLifecycleEvent -import io.element.android.libraries.theme.ElementTheme +import io.element.android.libraries.mediaviewer.api.helper.formatFileExtensionAndSize +import io.element.android.libraries.ui.strings.CommonStrings import me.saket.telephoto.zoomable.ZoomSpec import me.saket.telephoto.zoomable.ZoomableState import me.saket.telephoto.zoomable.coil.ZoomableAsyncImage @@ -135,7 +137,7 @@ private fun MediaImageView( modifier = modifier.fillMaxSize(), state = zoomableImageState, model = localMedia?.uri, - contentDescription = "Image", + contentDescription = stringResource(id = CommonStrings.common_image), contentScale = ContentScale.Fit, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaViewState.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaViewState.kt similarity index 94% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaViewState.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaViewState.kt index d5af7a78e1..07f891c90c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaViewState.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaViewState.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.local +package io.element.android.libraries.mediaviewer.api.local import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/MediaInfo.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt similarity index 95% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/MediaInfo.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt index af0f142bd8..726c9dcf0b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/MediaInfo.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.local +package io.element.android.libraries.mediaviewer.api.local import android.os.Parcelable import io.element.android.libraries.core.mimetype.MimeTypes diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/exoplayer/ExoPlayerWrapper.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/exoplayer/ExoPlayerWrapper.kt similarity index 95% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/exoplayer/ExoPlayerWrapper.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/exoplayer/ExoPlayerWrapper.kt index a69db1ef2c..581a018c43 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/exoplayer/ExoPlayerWrapper.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/exoplayer/ExoPlayerWrapper.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.local.exoplayer +package io.element.android.libraries.mediaviewer.api.local.exoplayer import android.content.Context import androidx.media3.common.Player diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/ParcelFileDescriptorFactory.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/ParcelFileDescriptorFactory.kt similarity index 94% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/ParcelFileDescriptorFactory.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/ParcelFileDescriptorFactory.kt index 22233b313f..523e8b593c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/ParcelFileDescriptorFactory.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/ParcelFileDescriptorFactory.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.local.pdf +package io.element.android.libraries.mediaviewer.api.local.pdf import android.content.Context import android.net.Uri diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfPage.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfPage.kt similarity index 98% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfPage.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfPage.kt index 0b8caed968..1da6d1a21c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfPage.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfPage.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.local.pdf +package io.element.android.libraries.mediaviewer.api.local.pdf import android.graphics.Bitmap import android.graphics.Canvas diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfRendererManager.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfRendererManager.kt similarity index 97% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfRendererManager.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfRendererManager.kt index 8f6c507eb5..56fe175647 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfRendererManager.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfRendererManager.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.local.pdf +package io.element.android.libraries.mediaviewer.api.local.pdf import android.graphics.pdf.PdfRenderer import android.os.ParcelFileDescriptor diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfViewer.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewer.kt similarity index 93% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfViewer.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewer.kt index 41a111d97d..1bad0e75f5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfViewer.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewer.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.local.pdf +package io.element.android.libraries.mediaviewer.api.local.pdf import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -35,9 +35,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.text.roundToPx import io.element.android.libraries.designsystem.text.toDp +import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import me.saket.telephoto.zoomable.zoomable @@ -98,7 +100,7 @@ private fun PdfPageView( is PdfPage.State.Loaded -> { Image( bitmap = state.bitmap.asImageBitmap(), - contentDescription = "Page ${pdfPage.pageIndex}", + contentDescription = stringResource(id = CommonStrings.a11y_page_n, pdfPage.pageIndex), contentScale = ContentScale.FillWidth, modifier = modifier.fillMaxWidth() ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfViewerState.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewerState.kt similarity index 97% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfViewerState.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewerState.kt index f64374d478..8df8bfdecf 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/pdf/PdfViewerState.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewerState.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.local.pdf +package io.element.android.libraries.mediaviewer.api.local.pdf import android.content.Context import androidx.compose.foundation.lazy.LazyListState diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/FileExtensionExtractor.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractor.kt similarity index 96% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/FileExtensionExtractor.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractor.kt index bdb4e0a23d..81a727909a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/FileExtensionExtractor.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractor.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.timeline.util +package io.element.android.libraries.mediaviewer.api.util import android.webkit.MimeTypeMap import com.squareup.anvil.annotations.ContributesBinding diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerEvents.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerEvents.kt similarity index 93% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerEvents.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerEvents.kt index a3d9632f18..b9f8b0aa4d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerEvents.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerEvents.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.viewer +package io.element.android.libraries.mediaviewer.api.viewer sealed interface MediaViewerEvents { data object SaveOnDisk: MediaViewerEvents diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerNode.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerNode.kt similarity index 86% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerNode.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerNode.kt index 78ecc2821b..3917d2eba4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerNode.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerNode.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.viewer +package io.element.android.libraries.mediaviewer.api.viewer import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -24,15 +24,15 @@ import com.bumble.appyx.core.plugin.Plugin import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode -import io.element.android.features.messages.impl.media.local.MediaInfo +import io.element.android.compound.theme.ForcedDarkElementTheme import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs -import io.element.android.libraries.theme.ForcedDarkElementTheme import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.mediaviewer.api.local.MediaInfo @ContributesNode(RoomScope::class) -class MediaViewerNode @AssistedInject constructor( +open class MediaViewerNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, presenterFactory: MediaViewerPresenter.Factory, @@ -42,6 +42,8 @@ class MediaViewerNode @AssistedInject constructor( val mediaInfo: MediaInfo, val mediaSource: MediaSource, val thumbnailSource: MediaSource?, + val canDownload: Boolean, + val canShare: Boolean, ) : NodeInputs private val inputs: Inputs = inputs() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt similarity index 94% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt index 91fff1fa72..90d81a4d0c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.viewer +package io.element.android.libraries.mediaviewer.api.viewer import android.content.ActivityNotFoundException import androidx.compose.runtime.Composable @@ -29,9 +29,6 @@ import androidx.compose.runtime.setValue import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import io.element.android.features.messages.impl.media.local.LocalMedia -import io.element.android.features.messages.impl.media.local.LocalMediaActions -import io.element.android.features.messages.impl.media.local.LocalMediaFactory import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher @@ -39,6 +36,9 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.media.MediaFile +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.api.local.LocalMediaActions +import io.element.android.libraries.mediaviewer.api.local.LocalMediaFactory import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -91,6 +91,8 @@ class MediaViewerPresenter @AssistedInject constructor( thumbnailSource = inputs.thumbnailSource, downloadedMedia = localMedia.value, snackbarMessage = snackbarMessage, + canDownload = inputs.canDownload, + canShare = inputs.canShare, eventSink = ::handleEvents ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerState.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerState.kt similarity index 80% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerState.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerState.kt index 86abaf648e..4b68f4afcc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerState.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerState.kt @@ -14,18 +14,20 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.viewer +package io.element.android.libraries.mediaviewer.api.viewer -import io.element.android.features.messages.impl.media.local.LocalMedia -import io.element.android.features.messages.impl.media.local.MediaInfo import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.api.local.MediaInfo data class MediaViewerState( val mediaInfo: MediaInfo, val thumbnailSource: MediaSource?, val downloadedMedia: Async, val snackbarMessage: SnackbarMessage?, + val canDownload: Boolean, + val canShare: Boolean, val eventSink: (MediaViewerEvents) -> Unit, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt similarity index 72% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt index 1042261be8..14f792c2c6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt @@ -14,18 +14,18 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.viewer +package io.element.android.libraries.mediaviewer.api.viewer import android.net.Uri import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.features.messages.impl.media.local.LocalMedia -import io.element.android.features.messages.impl.media.local.MediaInfo -import io.element.android.features.messages.impl.media.local.aFileInfo -import io.element.android.features.messages.impl.media.local.aPdfInfo -import io.element.android.features.messages.impl.media.local.aVideoInfo -import io.element.android.features.messages.impl.media.local.anAudioInfo -import io.element.android.features.messages.impl.media.local.anImageInfo import io.element.android.libraries.architecture.Async +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.api.local.MediaInfo +import io.element.android.libraries.mediaviewer.api.local.aFileInfo +import io.element.android.libraries.mediaviewer.api.local.aPdfInfo +import io.element.android.libraries.mediaviewer.api.local.aVideoInfo +import io.element.android.libraries.mediaviewer.api.local.anAudioInfo +import io.element.android.libraries.mediaviewer.api.local.anImageInfo open class MediaViewerStateProvider : PreviewParameterProvider { override val values: Sequence @@ -71,15 +71,27 @@ open class MediaViewerStateProvider : PreviewParameterProvider ), anAudioInfo(), ), + aMediaViewerState( + Async.Success( + LocalMedia(Uri.EMPTY, anImageInfo()) + ), + anImageInfo(), + canDownload = false, + canShare = false, + ), ) } fun aMediaViewerState( downloadedMedia: Async = Async.Uninitialized, mediaInfo: MediaInfo = anImageInfo(), + canDownload: Boolean = true, + canShare: Boolean = true, ) = MediaViewerState( mediaInfo = mediaInfo, thumbnailSource = null, downloadedMedia = downloadedMedia, - snackbarMessage = null + snackbarMessage = null, + canDownload = canDownload, + canShare = canShare, ) {} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt similarity index 84% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt rename to libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt index cb2775c7b6..d7f743f7ad 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt @@ -16,7 +16,7 @@ @file:OptIn(ExperimentalMaterial3Api::class) -package io.element.android.features.messages.impl.media.viewer +package io.element.android.libraries.mediaviewer.api.viewer import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn @@ -47,25 +47,25 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import coil.compose.AsyncImage -import io.element.android.features.messages.impl.R -import io.element.android.features.messages.impl.media.local.LocalMedia -import io.element.android.features.messages.impl.media.local.LocalMediaView -import io.element.android.features.messages.impl.media.local.MediaInfo -import io.element.android.features.messages.impl.media.local.rememberLocalMediaViewState import io.element.android.libraries.architecture.Async import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.RetryDialog +import io.element.android.libraries.designsystem.icons.CompoundDrawables import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.ui.media.MediaRequestData +import io.element.android.libraries.mediaviewer.api.R +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.api.local.LocalMediaView +import io.element.android.libraries.mediaviewer.api.local.MediaInfo +import io.element.android.libraries.mediaviewer.api.local.rememberLocalMediaViewState import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.delay @@ -96,6 +96,8 @@ fun MediaViewerView( actionsEnabled = state.downloadedMedia is Async.Success, mimeType = state.mediaInfo.mimeType, onBackPressed = onBackPressed, + canDownload = state.canDownload, + canShare = state.canShare, eventSink = state.eventSink ) }, @@ -162,9 +164,12 @@ private fun rememberShowProgress(downloadedMedia: Async): Boolean { return showProgress } +@OptIn(ExperimentalMaterial3Api::class) @Composable private fun MediaViewerTopBar( actionsEnabled: Boolean, + canDownload: Boolean, + canShare: Boolean, mimeType: String, onBackPressed: () -> Unit, eventSink: (MediaViewerEvents) -> Unit, @@ -190,27 +195,31 @@ private fun MediaViewerTopBar( ) } } - IconButton( - enabled = actionsEnabled, - onClick = { - eventSink(MediaViewerEvents.SaveOnDisk) - }, - ) { - Icon( - resourceId = CommonDrawables.ic_compound_download, - contentDescription = stringResource(id = CommonStrings.action_save), - ) + if (canDownload) { + IconButton( + enabled = actionsEnabled, + onClick = { + eventSink(MediaViewerEvents.SaveOnDisk) + }, + ) { + Icon( + resourceId = CompoundDrawables.ic_download, + contentDescription = stringResource(id = CommonStrings.action_save), + ) + } } - IconButton( - enabled = actionsEnabled, - onClick = { - eventSink(MediaViewerEvents.Share) - }, - ) { - Icon( - resourceId = CommonDrawables.ic_compound_share_android, - contentDescription = stringResource(id = CommonStrings.action_share) - ) + if (canShare) { + IconButton( + enabled = actionsEnabled, + onClick = { + eventSink(MediaViewerEvents.Share) + }, + ) { + Icon( + resourceId = CompoundDrawables.ic_share_android, + contentDescription = stringResource(id = CommonStrings.action_share) + ) + } } } ) diff --git a/features/messages/impl/src/main/res/drawable/ic_apk_install.xml b/libraries/mediaviewer/api/src/main/res/drawable/ic_apk_install.xml similarity index 100% rename from features/messages/impl/src/main/res/drawable/ic_apk_install.xml rename to libraries/mediaviewer/api/src/main/res/drawable/ic_apk_install.xml diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt b/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/MediaViewerPresenterTest.kt similarity index 90% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt rename to libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/MediaViewerPresenterTest.kt index 79c865ce92..ce2b4651a5 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt +++ b/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/MediaViewerPresenterTest.kt @@ -16,23 +16,23 @@ @file:OptIn(ExperimentalCoroutinesApi::class) -package io.element.android.features.messages.media.viewer +package io.element.android.libraries.mediaviewer import android.net.Uri import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import io.element.android.features.messages.impl.media.local.aFileInfo -import io.element.android.features.messages.impl.media.viewer.MediaViewerEvents -import io.element.android.features.messages.impl.media.viewer.MediaViewerNode -import io.element.android.features.messages.impl.media.viewer.MediaViewerPresenter -import io.element.android.features.messages.media.FakeLocalMediaActions -import io.element.android.features.messages.media.FakeLocalMediaFactory import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.matrix.test.media.FakeMediaLoader import io.element.android.libraries.matrix.test.media.aMediaSource +import io.element.android.libraries.mediaviewer.api.local.aFileInfo +import io.element.android.libraries.mediaviewer.test.FakeLocalMediaActions +import io.element.android.libraries.mediaviewer.test.FakeLocalMediaFactory +import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerEvents +import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode +import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerPresenter import io.element.android.tests.testutils.WarmUpRule import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -151,12 +151,16 @@ class MediaViewerPresenterTest { mediaLoader: FakeMediaLoader, localMediaActions: FakeLocalMediaActions, snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(), + canShare: Boolean = true, + canDownload: Boolean = true, ): MediaViewerPresenter { return MediaViewerPresenter( inputs = MediaViewerNode.Inputs( mediaInfo = TESTED_MEDIA_INFO, mediaSource = aMediaSource(), - thumbnailSource = null + thumbnailSource = null, + canShare = canShare, + canDownload = canDownload, ), localMediaFactory = localMediaFactory, mediaLoader = mediaLoader, diff --git a/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractorTest.kt b/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractorTest.kt new file mode 100644 index 0000000000..a2795d2884 --- /dev/null +++ b/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractorTest.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.mediaviewer.api.util + +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class FileExtensionExtractorTest { + @Test + fun `test FileExtensionExtractor with validation OK`() { + val sut = FileExtensionExtractorWithValidation() + // The result should be txt, but with Robolectric, + // MimeTypeMap.getSingleton().hasExtension() always returns false + assertThat(sut.extractFromName("test.txt")).isEqualTo("bin") + } + + @Test + fun `test FileExtensionExtractor with validation ERROR`() { + val sut = FileExtensionExtractorWithValidation() + assertThat(sut.extractFromName("test.bla")).isEqualTo("bin") + } + + @Test + fun `test FileExtensionExtractor no validation`() { + val sut = FileExtensionExtractorWithoutValidation() + assertThat(sut.extractFromName("test.png")).isEqualTo("png") + assertThat(sut.extractFromName("test.bla")).isEqualTo("bla") + } +} diff --git a/libraries/mediaviewer/impl/build.gradle.kts b/libraries/mediaviewer/impl/build.gradle.kts new file mode 100644 index 0000000000..0ccfd0b65e --- /dev/null +++ b/libraries/mediaviewer/impl/build.gradle.kts @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 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. + */ +plugins { + id("io.element.android-compose-library") + alias(libs.plugins.anvil) + id("kotlin-parcelize") +} + +android { + namespace = "io.element.android.libraries.mediaviewer.impl" +} + +anvil { + generateDaggerFactories.set(true) +} + +dependencies { + anvil(projects.anvilcodegen) + implementation(projects.anvilannotations) + + implementation(libs.coroutines.core) + implementation(libs.dagger) + + api(projects.libraries.mediaviewer.api) + implementation(projects.libraries.androidutils) + implementation(projects.libraries.core) + implementation(projects.libraries.di) + implementation(projects.libraries.matrix.api) + + testImplementation(projects.libraries.matrix.test) + testImplementation(projects.libraries.mediaviewer.test) + testImplementation(projects.tests.testutils) + testImplementation(libs.test.junit) + testImplementation(libs.test.truth) + testImplementation(libs.test.mockk) + testImplementation(libs.molecule.runtime) + testImplementation(libs.test.turbine) + testImplementation(libs.test.robolectric) + testImplementation(libs.test.turbine) + testImplementation(libs.coroutines.core) + testImplementation(libs.coroutines.test) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaActions.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt similarity index 97% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaActions.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt index ef93574134..35ebc0e022 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaActions.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.local +package io.element.android.libraries.mediaviewer.impl.local import android.app.Activity import android.content.ContentResolver @@ -43,6 +43,8 @@ import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.ApplicationContext +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.api.local.LocalMediaActions import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import timber.log.Timber diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaFactory.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt similarity index 88% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaFactory.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt index ff2f8aaeeb..1c026ce53b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaFactory.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt @@ -14,22 +14,25 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.media.local +package io.element.android.libraries.mediaviewer.impl.local import android.content.Context import android.net.Uri import androidx.core.net.toUri import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.features.messages.impl.timeline.util.FileExtensionExtractor -import io.element.android.libraries.androidutils.filesize.FileSizeFormatter import io.element.android.libraries.androidutils.file.getFileName import io.element.android.libraries.androidutils.file.getFileSize import io.element.android.libraries.androidutils.file.getMimeType +import io.element.android.libraries.androidutils.filesize.FileSizeFormatter import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.matrix.api.media.MediaFile import io.element.android.libraries.matrix.api.media.toFile +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.api.local.LocalMediaFactory +import io.element.android.libraries.mediaviewer.api.local.MediaInfo +import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractor import javax.inject.Inject @ContributesBinding(AppScope::class) diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActionsTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActionsTest.kt new file mode 100644 index 0000000000..0e981d73af --- /dev/null +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActionsTest.kt @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.mediaviewer.impl.local + +import android.net.Uri +import androidx.activity.compose.LocalActivityResultRegistryOwner +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.platform.LocalContext +import app.cash.molecule.RecompositionMode +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.test.core.aBuildMeta +import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia +import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +@RunWith(RobolectricTestRunner::class) +class AndroidLocalMediaActionsTest { + + @Test + fun `present - AndroidLocalMediaAction configure`() = runTest { + val sut = createAndroidLocalMediaActions() + moleculeFlow(RecompositionMode.Immediate) { + CompositionLocalProvider( + LocalContext provides RuntimeEnvironment.getApplication(), + LocalActivityResultRegistryOwner provides NoOpActivityResultRegistryOwner() + ) { + sut.Configure() + } + }.test { + awaitItem() + } + } + + @Test + fun `test AndroidLocalMediaAction share`() = runTest { + val sut = createAndroidLocalMediaActions() + val result = sut.share(aLocalMedia(Uri.parse("file://afile"))) + assertThat(result.exceptionOrNull()).isNotNull() + } + + @Test + fun `test AndroidLocalMediaAction open`() = runTest { + val sut = createAndroidLocalMediaActions() + val result = sut.open(aLocalMedia(Uri.parse("file://afile"))) + assertThat(result.exceptionOrNull()).isNotNull() + } + + @Test + fun `test AndroidLocalMediaAction save on disk`() = runTest { + val sut = createAndroidLocalMediaActions() + val result = sut.saveOnDisk(aLocalMedia(Uri.parse("file://afile"))) + assertThat(result.exceptionOrNull()).isNotNull() + } + + private fun TestScope.createAndroidLocalMediaActions() = AndroidLocalMediaActions( + RuntimeEnvironment.getApplication(), + testCoroutineDispatchers(), + aBuildMeta() + ) +} diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt new file mode 100644 index 0000000000..c6209ae7c3 --- /dev/null +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.mediaviewer.impl.local + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter +import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.matrix.api.media.MediaFile +import io.element.android.libraries.matrix.test.media.FakeMediaFile +import io.element.android.libraries.mediaviewer.api.local.MediaInfo +import io.element.android.libraries.mediaviewer.api.local.anImageInfo +import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorWithoutValidation +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +@RunWith(RobolectricTestRunner::class) +class AndroidLocalMediaFactoryTest { + + @Test + fun `test AndroidLocalMediaFactory`() { + val sut = createAndroidLocalMediaFactory() + val result = sut.createFromMediaFile(aMediaFile(), anImageInfo()) + assertThat(result.uri.toString()).endsWith("aPath") + assertThat(result.info).isEqualTo( + MediaInfo( + name = "an image file.jpg", + mimeType = MimeTypes.Jpeg, + formattedFileSize = "4MB", + fileExtension = "jpg", + ) + ) + } + + private fun aMediaFile(): MediaFile { + return FakeMediaFile("aPath") + } + + private fun createAndroidLocalMediaFactory(): AndroidLocalMediaFactory { + return AndroidLocalMediaFactory( + RuntimeEnvironment.getApplication(), + FakeFileSizeFormatter(), + FileExtensionExtractorWithoutValidation() + ) + } +} diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/NoOpActivityResultRegistryOwner.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/NoOpActivityResultRegistryOwner.kt new file mode 100644 index 0000000000..9d774bb163 --- /dev/null +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/NoOpActivityResultRegistryOwner.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.mediaviewer.impl.local + +import androidx.activity.result.ActivityResultRegistry +import androidx.activity.result.ActivityResultRegistryOwner +import androidx.activity.result.contract.ActivityResultContract +import androidx.core.app.ActivityOptionsCompat + +class NoOpActivityResultRegistryOwner : ActivityResultRegistryOwner { + override val activityResultRegistry: ActivityResultRegistry + get() = NoOpActivityResultRegistry() +} + +class NoOpActivityResultRegistry : ActivityResultRegistry() { + override fun onLaunch( + requestCode: Int, + contract: ActivityResultContract, + input: I, + options: ActivityOptionsCompat?, + ) = Unit +} diff --git a/libraries/mediaviewer/test/build.gradle.kts b/libraries/mediaviewer/test/build.gradle.kts new file mode 100644 index 0000000000..6bbedd7f1d --- /dev/null +++ b/libraries/mediaviewer/test/build.gradle.kts @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023 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. + */ + +plugins { + id("io.element.android-compose-library") +} + +android { + namespace = "io.element.android.libraries.mediaviewer.test" +} + +dependencies { + api(projects.libraries.mediaviewer.impl) + implementation(projects.libraries.core) + implementation(projects.tests.testutils) + implementation(projects.libraries.matrix.api) +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/FakeLocalMediaActions.kt b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaActions.kt similarity index 88% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/FakeLocalMediaActions.kt rename to libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaActions.kt index 5bdef5f9b1..8c303de541 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/FakeLocalMediaActions.kt +++ b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaActions.kt @@ -14,11 +14,11 @@ * limitations under the License. */ -package io.element.android.features.messages.media +package io.element.android.libraries.mediaviewer.test import androidx.compose.runtime.Composable -import io.element.android.features.messages.impl.media.local.LocalMedia -import io.element.android.features.messages.impl.media.local.LocalMediaActions +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.api.local.LocalMediaActions import io.element.android.tests.testutils.simulateLongTask class FakeLocalMediaActions : LocalMediaActions { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/FakeLocalMediaFactory.kt b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt similarity index 77% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/FakeLocalMediaFactory.kt rename to libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt index b5efdaf77d..84f1d87613 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/FakeLocalMediaFactory.kt +++ b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt @@ -14,17 +14,17 @@ * limitations under the License. */ -package io.element.android.features.messages.media +package io.element.android.libraries.mediaviewer.test import android.net.Uri -import io.element.android.features.messages.fixtures.aLocalMedia -import io.element.android.features.messages.impl.media.local.LocalMedia -import io.element.android.features.messages.impl.media.local.LocalMediaFactory -import io.element.android.features.messages.impl.media.local.MediaInfo -import io.element.android.features.messages.impl.timeline.util.FileExtensionExtractor -import io.element.android.features.messages.impl.timeline.util.FileExtensionExtractorWithoutValidation import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.media.MediaFile +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.api.local.LocalMediaFactory +import io.element.android.libraries.mediaviewer.api.local.MediaInfo +import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia +import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractor +import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorWithoutValidation class FakeLocalMediaFactory( private val localMediaUri: Uri, diff --git a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/viewer/media.kt b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/viewer/media.kt new file mode 100644 index 0000000000..e19113c163 --- /dev/null +++ b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/viewer/media.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.mediaviewer.test.viewer + +import android.net.Uri +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.api.local.MediaInfo +import io.element.android.libraries.mediaviewer.api.local.anImageInfo + +fun aLocalMedia( + uri: Uri, + mediaInfo: MediaInfo = anImageInfo(), +) = LocalMedia( + uri = uri, + info = mediaInfo +) + diff --git a/libraries/permissions/api/src/main/res/values-hu/translations.xml b/libraries/permissions/api/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..de3573fce2 --- /dev/null +++ b/libraries/permissions/api/src/main/res/values-hu/translations.xml @@ -0,0 +1,7 @@ + + + "Hogy az alkalmazás használhassa a kamerát, adja meg az engedélyt a rendszerbeállításokban." + "Adja meg az engedélyt a rendszerbeállításokban." + "Hogy az alkalmazás használhassa a mikrofont, adja meg az engedélyt a rendszerbeállításokban." + "Hogy az alkalmazás megjeleníthesse az értesítéseket, adja meg az engedélyt a rendszerbeállításokban." + diff --git a/libraries/permissions/api/src/main/res/values-in/translations.xml b/libraries/permissions/api/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..40fe4b0390 --- /dev/null +++ b/libraries/permissions/api/src/main/res/values-in/translations.xml @@ -0,0 +1,7 @@ + + + "Supaya aplikasinya dapat menggunakan kamera, berikan izin dalam pengaturan sistem." + "Silakan memberikan izin dalam pengaturan sistem." + "Supaya aplikasinya dapat menggunakan mikrofon, berikan izin dalam pengaturan sistem." + "Supaya aplikasinya dapat menampilkan notifikasi, berikan izin dalam pengaturan sistem." + diff --git a/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/FakeComposablePermissionStateProvider.kt b/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/FakeComposablePermissionStateProvider.kt index 900d9ccc69..948cc603c3 100644 --- a/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/FakeComposablePermissionStateProvider.kt +++ b/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/FakeComposablePermissionStateProvider.kt @@ -27,7 +27,7 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.PermissionState import com.google.accompanist.permissions.PermissionStatus -class FakeComposablePermissionStateProvider constructor( +class FakeComposablePermissionStateProvider( private val permissionState: FakePermissionState ) : ComposablePermissionStateProvider { private lateinit var onPermissionResult: (Boolean) -> Unit diff --git a/libraries/push/impl/build.gradle.kts b/libraries/push/impl/build.gradle.kts index c67d805fb6..f72a0886a0 100644 --- a/libraries/push/impl/build.gradle.kts +++ b/libraries/push/impl/build.gradle.kts @@ -21,6 +21,12 @@ plugins { android { namespace = "io.element.android.libraries.push.impl" + + testOptions { + unitTests { + isIncludeAndroidResources = true + } + } } anvil { @@ -55,10 +61,15 @@ dependencies { implementation(projects.services.toolbox.api) testImplementation(libs.test.junit) + testImplementation(libs.test.robolectric) testImplementation(libs.test.mockk) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) + testImplementation(libs.coil.test) testImplementation(libs.coroutines.test) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.tests.testutils) testImplementation(projects.services.appnavstate.test) + testImplementation(projects.services.toolbox.impl) + testImplementation(projects.services.toolbox.test) } diff --git a/libraries/push/impl/src/main/AndroidManifest.xml b/libraries/push/impl/src/main/AndroidManifest.xml index 6085ffe4a4..17bd098f90 100644 --- a/libraries/push/impl/src/main/AndroidManifest.xml +++ b/libraries/push/impl/src/main/AndroidManifest.xml @@ -15,6 +15,7 @@ --> + + + + + + + diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt index f2e3240203..904a6bf2ea 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt @@ -30,12 +30,14 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder import io.element.android.libraries.push.api.notifications.NotificationDrawerManager import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.element.android.services.appnavstate.api.AppNavigationStateService import io.element.android.services.appnavstate.api.NavigationState import io.element.android.services.appnavstate.api.currentSessionId import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -60,7 +62,10 @@ class DefaultNotificationDrawerManager @Inject constructor( private val dispatchers: CoroutineDispatchers, private val buildMeta: BuildMeta, private val matrixClientProvider: MatrixClientProvider, + private val imageLoaderHolder: ImageLoaderHolder, ) : NotificationDrawerManager { + private var appNavigationStateObserver: Job? = null + /** * Lazily initializes the NotificationState as we rely on having a current session in order to fetch the persisted queue of events. */ @@ -72,12 +77,17 @@ class DefaultNotificationDrawerManager @Inject constructor( init { // Observe application state - coroutineScope.launch { + appNavigationStateObserver = coroutineScope.launch { appNavigationStateService.appNavigationState .collect { onAppNavigationStateChange(it.navigationState) } } } + // For test only + fun destroy() { + appNavigationStateObserver?.cancel() + } + private var currentAppNavigationState: NavigationState? = null private fun onAppNavigationStateChange(navigationState: NavigationState) { @@ -280,10 +290,11 @@ class DefaultNotificationDrawerManager @Inject constructor( } eventsForSessions.forEach { (sessionId, notifiableEvents) -> + val client = matrixClientProvider.getOrRestore(sessionId).getOrThrow() + val imageLoader = imageLoaderHolder.get(client) val currentUser = tryOrNull( onError = { Timber.tag(loggerTag.value).e(it, "Unable to retrieve info for user ${sessionId.value}") }, operation = { - val client = matrixClientProvider.getOrRestore(sessionId).getOrThrow() // myUserDisplayName cannot be empty else NotificationCompat.MessagingStyle() will crash val myUserDisplayName = client.loadUserDisplayName().getOrNull() ?: sessionId.value val userAvatarUrl = client.loadUserAvatarURLString().getOrNull() @@ -299,7 +310,7 @@ class DefaultNotificationDrawerManager @Inject constructor( avatarUrl = null ) - notificationRenderer.render(currentUser, useCompleteNotificationFormat, notifiableEvents) + notificationRenderer.render(currentUser, useCompleteNotificationFormat, notifiableEvents, imageLoader) } } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationEventPersistence.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationEventPersistence.kt new file mode 100644 index 0000000000..3646837937 --- /dev/null +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationEventPersistence.kt @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2021 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 io.element.android.libraries.push.impl.notifications + +import android.content.Context +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.androidutils.file.EncryptedFileFactory +import io.element.android.libraries.androidutils.file.safeDelete +import io.element.android.libraries.core.data.tryOrNull +import io.element.android.libraries.core.log.logger.LoggerTag +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.ApplicationContext +import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent +import timber.log.Timber +import java.io.File +import java.io.ObjectInputStream +import java.io.ObjectOutputStream +import javax.inject.Inject + +private const val ROOMS_NOTIFICATIONS_FILE_NAME_LEGACY = "im.vector.notifications.cache" +private const val FILE_NAME = "notifications.bin" + +private val loggerTag = LoggerTag("NotificationEventPersistence", LoggerTag.NotificationLoggerTag) + +@ContributesBinding(AppScope::class) +class DefaultNotificationEventPersistence @Inject constructor( + @ApplicationContext private val context: Context, +) : NotificationEventPersistence { + private val file by lazy { + deleteLegacyFileIfAny() + context.getDatabasePath(FILE_NAME) + } + + private val encryptedFile by lazy { + EncryptedFileFactory(context).create(file) + } + + override fun loadEvents(factory: (List) -> NotificationEventQueue): NotificationEventQueue { + val rawEvents: ArrayList? = file + .takeIf { it.exists() } + ?.let { + try { + encryptedFile.openFileInput().use { fis -> + ObjectInputStream(fis).use { ois -> + @Suppress("UNCHECKED_CAST") + ois.readObject() as? ArrayList + } + }.also { + Timber.tag(loggerTag.value).d("Deserializing ${it?.size} NotifiableEvent(s)") + } + } catch (e: Throwable) { + Timber.tag(loggerTag.value).e(e, "## Failed to load cached notification info") + null + } + } + return factory(rawEvents.orEmpty()) + } + + override fun persistEvents(queuedEvents: NotificationEventQueue) { + Timber.tag(loggerTag.value).d("Serializing ${queuedEvents.rawEvents().size} NotifiableEvent(s)") + // Always delete file before writing, or encryptedFile.openFileOutput() will throw + file.safeDelete() + if (queuedEvents.isEmpty()) return + try { + encryptedFile.openFileOutput().use { fos -> + ObjectOutputStream(fos).use { oos -> + oos.writeObject(queuedEvents.rawEvents()) + } + } + } catch (e: Throwable) { + Timber.tag(loggerTag.value).e(e, "## Failed to save cached notification info") + } + } + + private fun deleteLegacyFileIfAny() { + tryOrNull { + File(context.applicationContext.cacheDir, ROOMS_NOTIFICATIONS_FILE_NAME_LEGACY).delete() + } + } +} diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt index 82b076aa73..017627305e 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt @@ -16,7 +16,12 @@ package io.element.android.libraries.push.impl.notifications +import android.content.Context +import android.net.Uri +import androidx.core.content.FileProvider import io.element.android.libraries.core.log.logger.LoggerTag +import io.element.android.libraries.di.ApplicationContext +import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId @@ -34,9 +39,9 @@ import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessa import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType import io.element.android.libraries.matrix.api.timeline.item.event.OtherMessageType import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType -import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType +import io.element.android.libraries.matrix.ui.messages.toPlainText import io.element.android.libraries.push.impl.R import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent @@ -60,6 +65,8 @@ class NotifiableEventResolver @Inject constructor( private val stringProvider: StringProvider, private val clock: SystemClock, private val matrixClientProvider: MatrixClientProvider, + private val notificationMediaRepoFactory: NotificationMediaRepo.Factory, + @ApplicationContext private val context: Context, ) { suspend fun resolveEvent(sessionId: SessionId, roomId: RoomId, eventId: EventId): NotifiableEvent? { @@ -75,12 +82,21 @@ class NotifiableEventResolver @Inject constructor( }.getOrNull() // TODO this notificationData is not always valid at the moment, sometimes the Rust SDK can't fetch the matching event - return notificationData?.asNotifiableEvent(sessionId) + return notificationData?.asNotifiableEvent(client, sessionId) } - private fun NotificationData.asNotifiableEvent(userId: SessionId): NotifiableEvent? { + private suspend fun NotificationData.asNotifiableEvent( + client: MatrixClient, + userId: SessionId, + ): NotifiableEvent? { return when (val content = this.content) { is NotificationContent.MessageLike.RoomMessage -> { + val messageBody = descriptionFromMessageContent(content, senderDisplayName ?: content.senderId.value) + val notificationBody = if (hasMention) { + stringProvider.getString(R.string.notification_mentioned_you_body, messageBody) + } else { + messageBody + } buildNotifiableMessageEvent( sessionId = userId, senderId = content.senderId, @@ -89,8 +105,8 @@ class NotifiableEventResolver @Inject constructor( noisy = isNoisy, timestamp = this.timestamp, senderName = senderDisplayName, - body = descriptionFromMessageContent(content, senderDisplayName ?: content.senderId.value), - imageUriString = this.contentUrl, + body = notificationBody, + imageUriString = fetchImageIfPresent(client)?.toString(), roomName = roomDisplayName, roomIsDirect = isDirect, roomAvatarPath = roomAvatarUrl, @@ -111,7 +127,7 @@ class NotifiableEventResolver @Inject constructor( soundName = null, isRedacted = false, isUpdated = false, - description = descriptionFromRoomMembershipContent(content, isDirect) ?: return null, + description = descriptionFromRoomMembershipInvite(isDirect), type = null, // TODO check if type is needed anymore title = null, // TODO check if title is needed anymore ) @@ -216,28 +232,49 @@ class NotifiableEventResolver @Inject constructor( is FileMessageType -> messageType.body is ImageMessageType -> messageType.body is NoticeMessageType -> messageType.body - is TextMessageType -> messageType.body + is TextMessageType -> messageType.toPlainText() is VideoMessageType -> messageType.body is LocationMessageType -> messageType.body is OtherMessageType -> messageType.body - is UnknownMessageType -> stringProvider.getString(CommonStrings.common_unsupported_event) } } - private fun descriptionFromRoomMembershipContent( - content: NotificationContent.StateEvent.RoomMemberContent, + private fun descriptionFromRoomMembershipInvite( isDirectRoom: Boolean - ): String? { - return when (content.membershipState) { - RoomMembershipState.INVITE -> { - if (isDirectRoom) { - stringProvider.getString(R.string.notification_invite_body) - } else { - stringProvider.getString(R.string.notification_room_invite_body) + ): String { + return if (isDirectRoom) { + stringProvider.getString(R.string.notification_invite_body) + } else { + stringProvider.getString(R.string.notification_room_invite_body) + } + } + + private suspend fun NotificationData.fetchImageIfPresent(client: MatrixClient): Uri? { + val fileResult = when (val content = this.content) { + is NotificationContent.MessageLike.RoomMessage -> { + when (val messageType = content.messageType) { + is ImageMessageType -> notificationMediaRepoFactory.create(client) + .getMediaFile( + mediaSource = messageType.source, + mimeType = messageType.info?.mimetype, + body = messageType.body, + ) + is VideoMessageType -> null // Use the thumbnail here? + else -> null } } else -> null - } + } ?: return null + + return fileResult + .onFailure { + Timber.tag(loggerTag.value).e(it, "Failed to download image for notification") + } + .map { mediaFile -> + val authority = "${context.packageName}.notifications.fileprovider" + FileProvider.getUriForFile(context, authority, mediaFile) + } + .getOrNull() } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt index 1ad38bf787..4c1e3043ad 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt @@ -21,37 +21,40 @@ import android.graphics.Bitmap import android.os.Build import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.toBitmap -import coil.imageLoader +import coil.ImageLoader import coil.request.ImageRequest import coil.transform.CircleCropTransformation import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.ui.media.MediaRequestData +import io.element.android.services.toolbox.api.sdk.BuildVersionSdkIntProvider import timber.log.Timber import javax.inject.Inject class NotificationBitmapLoader @Inject constructor( - @ApplicationContext private val context: Context + @ApplicationContext private val context: Context, + private val sdkIntProvider: BuildVersionSdkIntProvider, ) { /** * Get icon of a room. * @param path mxc url + * @param imageLoader Coil image loader */ - suspend fun getRoomBitmap(path: String?): Bitmap? { + suspend fun getRoomBitmap(path: String?, imageLoader: ImageLoader): Bitmap? { if (path == null) { return null } - return loadRoomBitmap(path) + return loadRoomBitmap(path, imageLoader) } - private suspend fun loadRoomBitmap(path: String): Bitmap? { + private suspend fun loadRoomBitmap(path: String, imageLoader: ImageLoader): Bitmap? { return try { val imageRequest = ImageRequest.Builder(context) .data(MediaRequestData(MediaSource(path), MediaRequestData.Kind.Thumbnail(1024))) .transformations(CircleCropTransformation()) .build() - val result = context.imageLoader.execute(imageRequest) + val result = imageLoader.execute(imageRequest) result.drawable?.toBitmap() } catch (e: Throwable) { Timber.e(e, "Unable to load room bitmap") @@ -63,22 +66,23 @@ class NotificationBitmapLoader @Inject constructor( * Get icon of a user. * Before Android P, this does nothing because the icon won't be used * @param path mxc url + * @param imageLoader Coil image loader */ - suspend fun getUserIcon(path: String?): IconCompat? { - if (path == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + suspend fun getUserIcon(path: String?, imageLoader: ImageLoader): IconCompat? { + if (path == null || sdkIntProvider.get() < Build.VERSION_CODES.P) { return null } - return loadUserIcon(path) + return loadUserIcon(path, imageLoader) } - private suspend fun loadUserIcon(path: String): IconCompat? { + private suspend fun loadUserIcon(path: String, imageLoader: ImageLoader): IconCompat? { return try { val imageRequest = ImageRequest.Builder(context) .data(MediaRequestData(MediaSource(path), MediaRequestData.Kind.Thumbnail(1024))) .transformations(CircleCropTransformation()) .build() - val result = context.imageLoader.execute(imageRequest) + val result = imageLoader.execute(imageRequest) val bitmap = result.drawable?.toBitmap() return bitmap?.let { IconCompat.createWithBitmap(it) } } catch (e: Throwable) { diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationEventPersistence.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationEventPersistence.kt index d1aee8c0b2..e593f49824 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationEventPersistence.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationEventPersistence.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 New Vector Ltd + * Copyright (c) 2023 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. @@ -16,76 +16,9 @@ package io.element.android.libraries.push.impl.notifications -import android.content.Context -import io.element.android.libraries.androidutils.file.EncryptedFileFactory -import io.element.android.libraries.androidutils.file.safeDelete -import io.element.android.libraries.core.data.tryOrNull -import io.element.android.libraries.core.log.logger.LoggerTag -import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent -import timber.log.Timber -import java.io.File -import java.io.ObjectInputStream -import java.io.ObjectOutputStream -import javax.inject.Inject -private const val ROOMS_NOTIFICATIONS_FILE_NAME_LEGACY = "im.vector.notifications.cache" -private const val FILE_NAME = "notifications.bin" - -private val loggerTag = LoggerTag("NotificationEventPersistence", LoggerTag.NotificationLoggerTag) - -class NotificationEventPersistence @Inject constructor( - @ApplicationContext private val context: Context, -) { - private val file by lazy { - deleteLegacyFileIfAny() - context.getDatabasePath(FILE_NAME) - } - - private val encryptedFile by lazy { - EncryptedFileFactory(context).create(file) - } - - fun loadEvents(factory: (List) -> NotificationEventQueue): NotificationEventQueue { - val rawEvents: ArrayList? = file - .takeIf { it.exists() } - ?.let { - try { - encryptedFile.openFileInput().use { fis -> - ObjectInputStream(fis).use { ois -> - @Suppress("UNCHECKED_CAST") - ois.readObject() as? ArrayList - } - }.also { - Timber.tag(loggerTag.value).d("Deserializing ${it?.size} NotifiableEvent(s)") - } - } catch (e: Throwable) { - Timber.tag(loggerTag.value).e(e, "## Failed to load cached notification info") - null - } - } - return factory(rawEvents.orEmpty()) - } - - fun persistEvents(queuedEvents: NotificationEventQueue) { - Timber.tag(loggerTag.value).d("Serializing ${queuedEvents.rawEvents().size} NotifiableEvent(s)") - // Always delete file before writing, or encryptedFile.openFileOutput() will throw - file.safeDelete() - if (queuedEvents.isEmpty()) return - try { - encryptedFile.openFileOutput().use { fos -> - ObjectOutputStream(fos).use { oos -> - oos.writeObject(queuedEvents.rawEvents()) - } - } - } catch (e: Throwable) { - Timber.tag(loggerTag.value).e(e, "## Failed to save cached notification info") - } - } - - private fun deleteLegacyFileIfAny() { - tryOrNull { - File(context.applicationContext.cacheDir, ROOMS_NOTIFICATIONS_FILE_NAME_LEGACY).delete() - } - } +interface NotificationEventPersistence { + fun loadEvents(factory: (List) -> NotificationEventQueue): NotificationEventQueue + fun persistEvents(queuedEvents: NotificationEventQueue) } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationEventQueue.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationEventQueue.kt index 8eea6a9d5a..bb76de47d2 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationEventQueue.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationEventQueue.kt @@ -28,7 +28,7 @@ import io.element.android.libraries.push.impl.notifications.model.NotifiableMess import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent import timber.log.Timber -data class NotificationEventQueue constructor( +data class NotificationEventQueue( private val queue: MutableList, /** * An in memory FIFO cache of the seen events. diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactory.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactory.kt index 859bff17cf..4abdc03123 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactory.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactory.kt @@ -17,9 +17,10 @@ package io.element.android.libraries.push.impl.notifications import android.app.Notification +import coil.ImageLoader import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.user.MatrixUser -import io.element.android.libraries.push.impl.notifications.factories.NotificationFactory +import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent @@ -28,15 +29,15 @@ import javax.inject.Inject private typealias ProcessedMessageEvents = List> -// TODO Find a better name, it clashes with io.element.android.libraries.push.impl.notifications.factories.NotificationFactory class NotificationFactory @Inject constructor( - private val notificationFactory: NotificationFactory, + private val notificationCreator: NotificationCreator, private val roomGroupMessageCreator: RoomGroupMessageCreator, private val summaryGroupMessageCreator: SummaryGroupMessageCreator ) { suspend fun Map.toNotifications( currentUser: MatrixUser, + imageLoader: ImageLoader, ): List { return map { (roomId, events) -> when { @@ -47,6 +48,7 @@ class NotificationFactory @Inject constructor( currentUser = currentUser, events = messageEvents, roomId = roomId, + imageLoader = imageLoader, ) } } @@ -65,7 +67,7 @@ class NotificationFactory @Inject constructor( when (processed) { ProcessedEvent.Type.REMOVE -> OneShotNotification.Removed(key = event.roomId.value) ProcessedEvent.Type.KEEP -> OneShotNotification.Append( - notificationFactory.createRoomInvitationNotification(event), + notificationCreator.createRoomInvitationNotification(event), OneShotNotification.Append.Meta( key = event.roomId.value, summaryLine = event.description, @@ -83,7 +85,7 @@ class NotificationFactory @Inject constructor( when (processed) { ProcessedEvent.Type.REMOVE -> OneShotNotification.Removed(key = event.eventId.value) ProcessedEvent.Type.KEEP -> OneShotNotification.Append( - notificationFactory.createSimpleEventNotification(event), + notificationCreator.createSimpleEventNotification(event), OneShotNotification.Append.Meta( key = event.eventId.value, summaryLine = event.description, @@ -100,7 +102,7 @@ class NotificationFactory @Inject constructor( when (processed) { ProcessedEvent.Type.REMOVE -> OneShotNotification.Removed(key = event.eventId.value) ProcessedEvent.Type.KEEP -> OneShotNotification.Append( - notificationFactory.createFallbackNotification(event), + notificationCreator.createFallbackNotification(event), OneShotNotification.Append.Meta( key = event.eventId.value, summaryLine = event.description.orEmpty(), diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt new file mode 100644 index 0000000000..505a4f4a3c --- /dev/null +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.push.impl.notifications + +import com.squareup.anvil.annotations.ContributesBinding +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.CacheDirectory +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrix.api.mxc.MxcTools +import java.io.File + +/** + * Fetches the media file for a notification. + * + * Media is downloaded from the rust sdk and stored in the application's cache directory. + * Media files are indexed by their Matrix Content (mxc://) URI and considered immutable. + * Whenever a given mxc is found in the cache, it is returned immediately. + */ +interface NotificationMediaRepo { + + /** + * Factory for [NotificationMediaRepo]. + */ + fun interface Factory { + /** + * Creates a [NotificationMediaRepo]. + * + */ + fun create( + client: MatrixClient + ): NotificationMediaRepo + } + + /** + * Returns the file. + * + * In case of a cache hit the file is returned immediately. + * In case of a cache miss the file is downloaded and then returned. + * + * @param mediaSource the media source of the media. + * @param mimeType the mime type of the media. + * @param body the body of the message. + * @return A [Result] holding either the media [File] from the cache directory or an [Exception]. + */ + suspend fun getMediaFile( + mediaSource: MediaSource, + mimeType: String?, + body: String?, + ): Result +} + +class DefaultNotificationMediaRepo @AssistedInject constructor( + @CacheDirectory private val cacheDir: File, + private val mxcTools: MxcTools, + @Assisted private val client: MatrixClient, +) : NotificationMediaRepo { + + @ContributesBinding(AppScope::class) + @AssistedFactory + fun interface Factory : NotificationMediaRepo.Factory { + override fun create( + client: MatrixClient, + ): DefaultNotificationMediaRepo + } + + private val matrixMediaLoader = client.mediaLoader + + override suspend fun getMediaFile( + mediaSource: MediaSource, + mimeType: String?, + body: String?, + ): Result { + val cachedFile = mediaSource.cachedFile() + return when { + cachedFile == null -> Result.failure(IllegalStateException("Invalid mxcUri.")) + cachedFile.exists() -> Result.success(cachedFile) + else -> matrixMediaLoader.downloadMediaFile( + source = mediaSource, + mimeType = mimeType, + body = body, + ).mapCatching { + it.use { mediaFile -> + val dest = cachedFile.apply { parentFile?.mkdirs() } + if (mediaFile.persist(dest.path)) { + dest + } else { + error("Failed to move file to cache.") + } + } + } + } + } + + private fun MediaSource.cachedFile(): File? = mxcTools.mxcUri2FilePath(url)?.let { + File("${cacheDir.path}/$CACHE_NOTIFICATION_SUBDIR/$it") + } +} + +/** + * Subdirectory of the application's cache directory where file are stored. + */ +private const val CACHE_NOTIFICATION_SUBDIR = "temp/notif" diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt index 713392c695..d05f3a1d03 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt @@ -16,6 +16,7 @@ package io.element.android.libraries.push.impl.notifications +import coil.ImageLoader import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.user.MatrixUser @@ -38,11 +39,12 @@ class NotificationRenderer @Inject constructor( suspend fun render( currentUser: MatrixUser, useCompleteNotificationFormat: Boolean, - eventsToProcess: List> + eventsToProcess: List>, + imageLoader: ImageLoader, ) { val groupedEvents = eventsToProcess.groupByType() with(notificationFactory) { - val roomNotifications = groupedEvents.roomEvents.toNotifications(currentUser) + val roomNotifications = groupedEvents.roomEvents.toNotifications(currentUser, imageLoader) val invitationNotifications = groupedEvents.invitationEvents.toNotifications() val simpleNotifications = groupedEvents.simpleEvents.toNotifications() val fallbackNotifications = groupedEvents.fallbackEvents.toNotifications() diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationsFileProvider.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationsFileProvider.kt new file mode 100644 index 0000000000..11999207ed --- /dev/null +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationsFileProvider.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.push.impl.notifications + +import androidx.core.content.FileProvider + +/** + * We have to declare our own file provider to avoid collision with other modules + * having their own. + */ +class NotificationsFileProvider : FileProvider() diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt index 29d828d34c..015b32c1d0 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt @@ -23,26 +23,27 @@ import androidx.core.app.NotificationCompat import androidx.core.app.Person import androidx.core.text.buildSpannedString import androidx.core.text.inSpans +import coil.ImageLoader import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.R import io.element.android.libraries.push.impl.notifications.debug.annotateForDebug -import io.element.android.libraries.push.impl.notifications.factories.NotificationFactory +import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent import io.element.android.services.toolbox.api.strings.StringProvider -import timber.log.Timber import javax.inject.Inject class RoomGroupMessageCreator @Inject constructor( private val bitmapLoader: NotificationBitmapLoader, private val stringProvider: StringProvider, - private val notificationFactory: NotificationFactory + private val notificationCreator: NotificationCreator ) { suspend fun createRoomMessage( currentUser: MatrixUser, events: List, roomId: RoomId, + imageLoader: ImageLoader, ): RoomNotification.Message { val lastKnownRoomEvent = events.last() val roomName = lastKnownRoomEvent.roomName ?: lastKnownRoomEvent.senderName ?: "Room name (${roomId.value.take(8)}…)" @@ -50,13 +51,13 @@ class RoomGroupMessageCreator @Inject constructor( val style = NotificationCompat.MessagingStyle( Person.Builder() .setName(currentUser.displayName?.annotateForDebug(50)) - .setIcon(bitmapLoader.getUserIcon(currentUser.avatarUrl)) + .setIcon(bitmapLoader.getUserIcon(currentUser.avatarUrl, imageLoader)) .setKey(lastKnownRoomEvent.sessionId.value) .build() ).also { it.conversationTitle = roomName.takeIf { roomIsGroup }?.annotateForDebug(51) it.isGroupConversation = roomIsGroup - it.addMessagesFromEvents(events) + it.addMessagesFromEvents(events, imageLoader) } val tickerText = if (roomIsGroup) { @@ -65,7 +66,7 @@ class RoomGroupMessageCreator @Inject constructor( stringProvider.getString(R.string.notification_ticker_text_dm, events.last().senderName, events.last().description) } - val largeBitmap = getRoomBitmap(events) + val largeBitmap = getRoomBitmap(events, imageLoader) val lastMessageTimestamp = events.last().timestamp val smartReplyErrors = events.filter { it.isSmartReplyError() } @@ -78,7 +79,7 @@ class RoomGroupMessageCreator @Inject constructor( shouldBing = events.any { it.noisy } ) return RoomNotification.Message( - notificationFactory.createMessagesListNotification( + notificationCreator.createMessagesListNotification( style, RoomEventGroupInfo( sessionId = currentUser.userId, @@ -99,14 +100,17 @@ class RoomGroupMessageCreator @Inject constructor( ) } - private suspend fun NotificationCompat.MessagingStyle.addMessagesFromEvents(events: List) { + private suspend fun NotificationCompat.MessagingStyle.addMessagesFromEvents( + events: List, + imageLoader: ImageLoader, + ) { events.forEach { event -> val senderPerson = if (event.outGoingMessage) { null } else { Person.Builder() .setName(event.senderName?.annotateForDebug(70)) - .setIcon(bitmapLoader.getUserIcon(event.senderAvatarPath)) + .setIcon(bitmapLoader.getUserIcon(event.senderAvatarPath, imageLoader)) .setKey(event.senderId.value) .build() } @@ -133,22 +137,16 @@ class RoomGroupMessageCreator @Inject constructor( } private fun createRoomMessagesGroupSummaryLine(events: List, roomName: String, roomIsDirect: Boolean): CharSequence { - return try { - when (events.size) { - 1 -> createFirstMessageSummaryLine(events.first(), roomName, roomIsDirect) - else -> { - stringProvider.getQuantityString( - R.plurals.notification_compat_summary_line_for_room, - events.size, - roomName, - events.size - ) - } + return when (events.size) { + 1 -> createFirstMessageSummaryLine(events.first(), roomName, roomIsDirect) + else -> { + stringProvider.getQuantityString( + R.plurals.notification_compat_summary_line_for_room, + events.size, + roomName, + events.size + ) } - } catch (e: Throwable) { - // String not found or bad format - Timber.v("%%%%%%%% REFRESH NOTIFICATION DRAWER failed to resolve string") - roomName } } @@ -166,7 +164,7 @@ class RoomGroupMessageCreator @Inject constructor( inSpans(StyleSpan(Typeface.BOLD)) { append(roomName) append(": ") - event.senderName + append(event.senderName) append(" ") } append(event.description) @@ -174,10 +172,13 @@ class RoomGroupMessageCreator @Inject constructor( } } - private suspend fun getRoomBitmap(events: List): Bitmap? { + private suspend fun getRoomBitmap( + events: List, + imageLoader: ImageLoader, + ): Bitmap? { // Use the last event (most recent?) return events.reversed().firstNotNullOfOrNull { it.roomAvatarPath } - ?.let { bitmapLoader.getRoomBitmap(it) } + ?.let { bitmapLoader.getRoomBitmap(it, imageLoader) } } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt index f999456107..f46df7d3a5 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt @@ -21,7 +21,7 @@ import androidx.core.app.NotificationCompat import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.R import io.element.android.libraries.push.impl.notifications.debug.annotateForDebug -import io.element.android.libraries.push.impl.notifications.factories.NotificationFactory +import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator import io.element.android.services.toolbox.api.strings.StringProvider import javax.inject.Inject @@ -41,7 +41,7 @@ import javax.inject.Inject */ class SummaryGroupMessageCreator @Inject constructor( private val stringProvider: StringProvider, - private val notificationFactory: NotificationFactory, + private val notificationCreator: NotificationCreator, ) { fun createSummaryNotification( @@ -77,7 +77,7 @@ class SummaryGroupMessageCreator @Inject constructor( // Use account name now, for multi-session .setSummaryText(currentUser.userId.value.annotateForDebug(44)) return if (useCompleteNotificationFormat) { - notificationFactory.createSummaryListNotification( + notificationCreator.createSummaryListNotification( currentUser, summaryInboxStyle, sumTitle, @@ -170,7 +170,7 @@ class SummaryGroupMessageCreator @Inject constructor( messageStr } } - return notificationFactory.createSummaryListNotification( + return notificationCreator.createSummaryListNotification( currentUser = currentUser, style = null, compatSummary = privacyTitle, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/TestNotificationReceiver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/TestNotificationReceiver.kt index a7135cee74..ec2cd227ca 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/TestNotificationReceiver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/TestNotificationReceiver.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright (c) 2023 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/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt index ab115262de..74020f9323 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt @@ -16,7 +16,6 @@ package io.element.android.libraries.push.impl.notifications.channels -import android.app.Activity import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context @@ -24,7 +23,6 @@ import android.os.Build import androidx.annotation.ChecksSdkIntAtLeast import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat -import io.element.android.libraries.androidutils.system.startNotificationChannelSettingsIntent import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.SingleIn @@ -163,17 +161,5 @@ class NotificationChannels @Inject constructor( @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O) private fun supportNotificationChannels() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O - - fun openSystemSettingsForSilentCategory(activity: Activity) { - activity.startNotificationChannelSettingsIntent(SILENT_NOTIFICATION_CHANNEL_ID) - } - - fun openSystemSettingsForNoisyCategory(activity: Activity) { - activity.startNotificationChannelSettingsIntent(NOISY_NOTIFICATION_CHANNEL_ID) - } - - fun openSystemSettingsForCallCategory(activity: Activity) { - activity.startNotificationChannelSettingsIntent(CALL_NOTIFICATION_CHANNEL_ID) - } } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationFactory.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt similarity index 99% rename from libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationFactory.kt rename to libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt index 105b5789e4..2beddc53f9 100755 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationFactory.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt @@ -41,7 +41,7 @@ import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiab import io.element.android.services.toolbox.api.strings.StringProvider import javax.inject.Inject -class NotificationFactory @Inject constructor( +class NotificationCreator @Inject constructor( @ApplicationContext private val context: Context, private val notificationChannels: NotificationChannels, private val stringProvider: StringProvider, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/action/MarkAsReadActionFactory.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/action/MarkAsReadActionFactory.kt index 0dcf4bb326..7e208f414a 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/action/MarkAsReadActionFactory.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/action/MarkAsReadActionFactory.kt @@ -20,9 +20,9 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import androidx.core.app.NotificationCompat +import io.element.android.appconfig.NotificationConfig import io.element.android.libraries.androidutils.uri.createIgnoredUri import io.element.android.libraries.di.ApplicationContext -import io.element.android.libraries.push.impl.NotificationConfig import io.element.android.libraries.push.impl.R import io.element.android.libraries.push.impl.notifications.NotificationActionIds import io.element.android.libraries.push.impl.notifications.NotificationBroadcastReceiver diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/action/QuickReplyActionFactory.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/action/QuickReplyActionFactory.kt index a5f72acfb9..fd89ce455f 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/action/QuickReplyActionFactory.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/action/QuickReplyActionFactory.kt @@ -22,12 +22,12 @@ import android.content.Intent import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.RemoteInput +import io.element.android.appconfig.NotificationConfig import io.element.android.libraries.androidutils.uri.createIgnoredUri import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.ThreadId -import io.element.android.libraries.push.impl.NotificationConfig import io.element.android.libraries.push.impl.R import io.element.android.libraries.push.impl.notifications.NotificationActionIds import io.element.android.libraries.push.impl.notifications.NotificationBroadcastReceiver diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt index 1b6bb8a67a..4b30bbe86d 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt @@ -58,9 +58,8 @@ data class NotifiableMessageEvent( override val description: String = body ?: "" val title: String = senderName ?: "" - // TODO EAx The image has to be downloaded and expose using the file provider. - // Example of value from Element Android: - // content://im.vector.app.debug.mx-sdk.fileprovider/downloads/downloads/816abf76d806c768760568952b1862c8/F/72c33edd23dee3b95f4d5a18aa25fa54/image.png + // Example of value: + // content://io.element.android.x.debug.notifications.fileprovider/downloads/temp/notif/matrix.org/XGItzSDOnSyXjYtOPfiKexDJ val imageUri: Uri? get() = imageUriString?.let { Uri.parse(it) } } diff --git a/libraries/push/impl/src/main/res/values-cs/translations.xml b/libraries/push/impl/src/main/res/values-cs/translations.xml index b9488e008b..2b76d7b93e 100644 --- a/libraries/push/impl/src/main/res/values-cs/translations.xml +++ b/libraries/push/impl/src/main/res/values-cs/translations.xml @@ -8,10 +8,7 @@ "Vstoupit" "Odmítnout" "Vás pozval(a) do chatu" - "%1$s vás zmínil(a). -%2$s" - "Byli jste zmíněni. -%1$s" + "Zmínili vás: %1$s" "Nové zprávy" "Reagoval(a) s %1$s" "Označit jako přečtené" diff --git a/libraries/push/impl/src/main/res/values-de/translations.xml b/libraries/push/impl/src/main/res/values-de/translations.xml index 1f05b0f171..a600cb83f7 100644 --- a/libraries/push/impl/src/main/res/values-de/translations.xml +++ b/libraries/push/impl/src/main/res/values-de/translations.xml @@ -8,6 +8,7 @@ "Beitreten" "Ablehnen" "Du wurdest zu einem Chat eingeladen" + "Hat Dich erwähnt: %1$s" "Neue Nachrichten" "Reagiert mit %1$s" "Als gelesen markieren" diff --git a/libraries/push/impl/src/main/res/values-fr/translations.xml b/libraries/push/impl/src/main/res/values-fr/translations.xml index 8ba38a31c3..daf8a092a6 100644 --- a/libraries/push/impl/src/main/res/values-fr/translations.xml +++ b/libraries/push/impl/src/main/res/values-fr/translations.xml @@ -8,6 +8,7 @@ "Rejoindre" "Rejeter" "Vous a invité(e) à discuter" + "Mentionné(e): %1$s" "Nouveaux messages" "A réagi avec %1$s" "Marquer comme lu" diff --git a/libraries/push/impl/src/main/res/values-hu/translations.xml b/libraries/push/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..f87455630b --- /dev/null +++ b/libraries/push/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,53 @@ + + + "Hívás" + "Események figyelése" + "Zajos értesítések" + "Csendes értesítések" + "** Nem sikerült elküldeni – nyissa meg a szobát" + "Csatlakozás" + "Elutasítás" + "Meghívta, hogy csevegjen" + "Megemlítette Önt: %1$s" + "Új üzenetek" + "Ezzel reagált: %1$s" + "Megjelölés olvasottként" + "Meghívta, hogy csatlakozzon a szobához" + "Én" + "Az értesítést nézi! Kattintson ide!" + "%1$s: %2$s" + "%1$s: %2$s %3$s" + "%1$s és %2$s" + "%1$s itt: %2$s" + "%1$s itt: %2$s és %3$s" + + "%1$s: %2$d üzenet" + "%1$s: %2$d üzenet" + + + "%d értesítés" + "%d értesítés" + + + "%d meghívó" + "%d meghívó" + + + "%d új üzenet" + "%d új üzenet" + + + "%d olvasatlan értesített üzenet" + "%d olvasatlan értesített üzenet" + + + "%d szoba" + "%d szoba" + + "Válassza ki az értesítések fogadásának módját" + "Háttérszinkronizálás" + "Google szolgáltatások" + "A Google Play szolgáltatások nem találhatók. Előfordulhat, hogy az értesítések nem működnek megfelelően." + "Értesítés" + "Gyors válasz" + diff --git a/libraries/push/impl/src/main/res/values-in/translations.xml b/libraries/push/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..0e3f3699f0 --- /dev/null +++ b/libraries/push/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,47 @@ + + + "Panggil" + "Mendengarkan peristiwa" + "Pemberitahuan berisik" + "Pemberitahuan diam" + "** Gagal mengirim — silakan buka ruangan" + "Bergabung" + "Tolak" + "Mengundang Anda untuk mengobrol" + "Menyebutkan Anda: %1$s" + "Pesan Baru" + "Menghapus dengan %1$s" + "Tandai sebagai dibaca" + "Mengundang Anda untuk bergabung ke ruangan" + "Saya" + "Anda sedang melihat pemberitahuan ini! Klik saya!" + "%1$s: %2$s" + "%1$s: %2$s %3$s" + "%1$s dan %2$s" + "%1$s di %2$s" + "%1$s di %2$s dan %3$s" + + "%1$s: %2$d pesan" + + + "%d pemberitahuan" + + + "%d undangan" + + + "%d pesan baru" + + + "%d pesan pemberitahuan yang belum dibaca" + + + "%d ruangan" + + "Pilih cara menerima notifikasi" + "Sinkronisasi latar belakang" + "Layanan Google" + "Tidak ditemukan Layanan Google Play yang valid. Pemberitahuan mungkin tidak berfungsi dengan baik." + "Notifikasi" + "Balas cepat" + diff --git a/libraries/push/impl/src/main/res/values-ru/translations.xml b/libraries/push/impl/src/main/res/values-ru/translations.xml index aec7180374..f183396dbc 100644 --- a/libraries/push/impl/src/main/res/values-ru/translations.xml +++ b/libraries/push/impl/src/main/res/values-ru/translations.xml @@ -8,10 +8,7 @@ "Присоединиться" "Отклонить" "Пригласил вас в чат" - "%1$s упомянул вас. -%2$s" - "Вас уже упомянули. -%1$s" + "Упомянул вас: %1$s" "Новые сообщения" "Отреагировал на %1$s" "Отметить как прочитанное" diff --git a/libraries/push/impl/src/main/res/values-sk/translations.xml b/libraries/push/impl/src/main/res/values-sk/translations.xml index 0bc3338204..bd79b42fc3 100644 --- a/libraries/push/impl/src/main/res/values-sk/translations.xml +++ b/libraries/push/impl/src/main/res/values-sk/translations.xml @@ -8,10 +8,7 @@ "Pripojiť sa" "Zamietnuť" "Vás pozval/a na konverzáciu" - "%1$s vás spomenul/-a. -%2$s" - "Boli ste spomenutí. -%1$s" + "Spomenul/a vás: %1$s" "Nové správy" "Reagoval/a s %1$s" "Označiť ako prečítané" diff --git a/libraries/push/impl/src/main/res/values/localazy.xml b/libraries/push/impl/src/main/res/values/localazy.xml index 5cadfb90ac..7d50cd2864 100644 --- a/libraries/push/impl/src/main/res/values/localazy.xml +++ b/libraries/push/impl/src/main/res/values/localazy.xml @@ -8,10 +8,7 @@ "Join" "Reject" "Invited you to chat" - "%1$s mentioned you. -%2$s" - "You have been mentioned. -%1$s" + "Mentioned you: %1$s" "New Messages" "Reacted with %1$s" "Mark as read" diff --git a/libraries/push/impl/src/main/res/xml/notifications_provider_paths.xml b/libraries/push/impl/src/main/res/xml/notifications_provider_paths.xml new file mode 100644 index 0000000000..7c15e41df3 --- /dev/null +++ b/libraries/push/impl/src/main/res/xml/notifications_provider_paths.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt new file mode 100644 index 0000000000..e641029c0d --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.push.impl.notifications + +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.matrix.test.A_SPACE_ID +import io.element.android.libraries.matrix.test.A_THREAD_ID +import io.element.android.libraries.matrix.test.FakeMatrixClientProvider +import io.element.android.libraries.matrix.test.core.aBuildMeta +import io.element.android.libraries.push.impl.notifications.fake.FakeAndroidNotificationFactory +import io.element.android.libraries.push.impl.notifications.fake.FakeImageLoaderHolder +import io.element.android.libraries.push.impl.notifications.fake.FakeRoomGroupMessageCreator +import io.element.android.libraries.push.impl.notifications.fake.FakeSummaryGroupMessageCreator +import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent +import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent +import io.element.android.services.appnavstate.api.AppNavigationState +import io.element.android.services.appnavstate.api.AppNavigationStateService +import io.element.android.services.appnavstate.api.NavigationState +import io.element.android.services.appnavstate.test.FakeAppNavigationStateService +import io.element.android.services.appnavstate.test.aNavigationState +import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(RobolectricTestRunner::class) +class DefaultNotificationDrawerManagerTest { + @Test + fun `clearAllEvents should have no effect when queue is empty`() = runTest { + val defaultNotificationDrawerManager = createDefaultNotificationDrawerManager() + defaultNotificationDrawerManager.clearAllEvents(A_SESSION_ID) + defaultNotificationDrawerManager.destroy() + } + + @Test + fun `cover all APIs`() = runTest { + // For now just call all the API. Later, add more valuable tests. + val defaultNotificationDrawerManager = createDefaultNotificationDrawerManager() + defaultNotificationDrawerManager.notificationStyleChanged() + defaultNotificationDrawerManager.clearAllMessagesEvents(A_SESSION_ID, doRender = true) + defaultNotificationDrawerManager.clearAllMessagesEvents(A_SESSION_ID, doRender = false) + defaultNotificationDrawerManager.clearEvent(A_SESSION_ID, AN_EVENT_ID, doRender = true) + defaultNotificationDrawerManager.clearEvent(A_SESSION_ID, AN_EVENT_ID, doRender = false) + defaultNotificationDrawerManager.clearMessagesForRoom(A_SESSION_ID, A_ROOM_ID, doRender = true) + defaultNotificationDrawerManager.clearMessagesForRoom(A_SESSION_ID, A_ROOM_ID, doRender = false) + defaultNotificationDrawerManager.clearMembershipNotificationForSession(A_SESSION_ID) + defaultNotificationDrawerManager.clearMembershipNotificationForRoom(A_SESSION_ID, A_ROOM_ID, doRender = true) + defaultNotificationDrawerManager.clearMembershipNotificationForRoom(A_SESSION_ID, A_ROOM_ID, doRender = false) + defaultNotificationDrawerManager.onNotifiableEventReceived(aNotifiableMessageEvent()) + // Add the same Event again (will be ignored) + defaultNotificationDrawerManager.onNotifiableEventReceived(aNotifiableMessageEvent()) + defaultNotificationDrawerManager.destroy() + } + + @Test + fun `react to applicationStateChange`() = runTest { + // For now just call all the API. Later, add more valuable tests. + val appNavigationStateFlow: MutableStateFlow = MutableStateFlow( + AppNavigationState( + navigationState = NavigationState.Root, + isInForeground = true, + ) + ) + val appNavigationStateService = FakeAppNavigationStateService(appNavigationState = appNavigationStateFlow) + val defaultNotificationDrawerManager = createDefaultNotificationDrawerManager( + appNavigationStateService = appNavigationStateService + ) + appNavigationStateFlow.emit(AppNavigationState(aNavigationState(), isInForeground = true)) + runCurrent() + appNavigationStateFlow.emit(AppNavigationState(aNavigationState(A_SESSION_ID), isInForeground = true)) + runCurrent() + appNavigationStateFlow.emit(AppNavigationState(aNavigationState(A_SESSION_ID, A_SPACE_ID), isInForeground = true)) + runCurrent() + appNavigationStateFlow.emit(AppNavigationState(aNavigationState(A_SESSION_ID, A_SPACE_ID, A_ROOM_ID), isInForeground = true)) + runCurrent() + appNavigationStateFlow.emit(AppNavigationState(aNavigationState(A_SESSION_ID, A_SPACE_ID, A_ROOM_ID, A_THREAD_ID), isInForeground = true)) + runCurrent() + // Like a user sign out + appNavigationStateFlow.emit(AppNavigationState(aNavigationState(), isInForeground = true)) + runCurrent() + defaultNotificationDrawerManager.destroy() + } + + private fun TestScope.createDefaultNotificationDrawerManager( + appNavigationStateService: AppNavigationStateService = FakeAppNavigationStateService(), + initialData: List = emptyList() + ): DefaultNotificationDrawerManager { + val context = RuntimeEnvironment.getApplication() + return DefaultNotificationDrawerManager( + notifiableEventProcessor = NotifiableEventProcessor( + outdatedDetector = OutdatedEventDetector(), + appNavigationStateService = appNavigationStateService + ), + notificationRenderer = NotificationRenderer( + NotificationIdProvider(), + NotificationDisplayer(context), + NotificationFactory( + FakeAndroidNotificationFactory().instance, + FakeRoomGroupMessageCreator().instance, + FakeSummaryGroupMessageCreator().instance, + ) + ), + notificationEventPersistence = InMemoryNotificationEventPersistence(initialData = initialData), + filteredEventDetector = FilteredEventDetector(), + appNavigationStateService = appNavigationStateService, + coroutineScope = this, + dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true), + buildMeta = aBuildMeta(), + matrixClientProvider = FakeMatrixClientProvider(), + imageLoaderHolder = FakeImageLoaderHolder(), + ) + } +} diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationEventPersistenceTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationEventPersistenceTest.kt new file mode 100644 index 0000000000..ce984c712f --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationEventPersistenceTest.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.push.impl.notifications + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.core.cache.CircularCache +import io.element.android.libraries.push.impl.notifications.fixtures.aSimpleNotifiableEvent +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +@RunWith(RobolectricTestRunner::class) +class DefaultNotificationEventPersistenceTest { + @Test + fun `loadEvents should return empty NotificationEventQueue`() { + val sut = createDefaultNotificationEventPersistence() + val result = sut.loadEvents( + factory = { rawEvents -> + NotificationEventQueue(rawEvents.toMutableList(), seenEventIds = CircularCache.create(cacheSize = 25)) + } + ) + assertThat(result.isEmpty()).isTrue() + } + + @Test + fun `after persisting NotificationEventQueue, loadEvents should return non-empty NotificationEventQueue`() { + val sut = createDefaultNotificationEventPersistence() + val notificationEventQueue = NotificationEventQueue(mutableListOf(), seenEventIds = CircularCache.create(cacheSize = 25)) + // First persist an empty queue + sut.persistEvents(notificationEventQueue) + // Add an event + notificationEventQueue.add(aSimpleNotifiableEvent()) + // Persist + // Note: is cannot work because AndroidKeyStore is not available. But we check that the code does + // not crash. + sut.persistEvents(notificationEventQueue) + sut.loadEvents( + factory = { rawEvents -> + NotificationEventQueue(rawEvents.toMutableList(), seenEventIds = CircularCache.create(cacheSize = 25)) + } + ) + // assertThat(result.isEmpty()).isFalse() + } + + private fun createDefaultNotificationEventPersistence(): DefaultNotificationEventPersistence { + val context = RuntimeEnvironment.getApplication() + return DefaultNotificationEventPersistence(context) + } +} diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/InMemoryNotificationEventPersistence.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/InMemoryNotificationEventPersistence.kt new file mode 100644 index 0000000000..09f907f50b --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/InMemoryNotificationEventPersistence.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.push.impl.notifications + +import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent + +class InMemoryNotificationEventPersistence( + initialData: List = emptyList() +) : NotificationEventPersistence { + private var data: List = initialData + + override fun loadEvents(factory: (List) -> NotificationEventQueue): NotificationEventQueue { + return factory(data) + } + + override fun persistEvents(queuedEvents: NotificationEventQueue) { + data = queuedEvents.rawEvents() + } +} diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolverTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolverTest.kt new file mode 100644 index 0000000000..bad49b64ef --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolverTest.kt @@ -0,0 +1,551 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.push.impl.notifications + +import android.content.Context +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrix.api.notification.NotificationContent +import io.element.android.libraries.matrix.api.notification.NotificationData +import io.element.android.libraries.matrix.api.room.RoomMembershipState +import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody +import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat +import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.AN_EVENT_ID_2 +import io.element.android.libraries.matrix.test.AN_EXCEPTION +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.matrix.test.A_USER_ID_2 +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.FakeMatrixClientProvider +import io.element.android.libraries.matrix.test.notification.FakeNotificationService +import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationMediaRepo +import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent +import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent +import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent +import io.element.android.services.toolbox.impl.strings.AndroidStringProvider +import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP +import io.element.android.services.toolbox.test.systemclock.FakeSystemClock +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +class NotifiableEventResolverTest { + + @Test + fun `resolve event no session`() = runTest { + val sut = createNotifiableEventResolver(notificationService = null) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + assertThat(result).isNull() + } + + @Test + fun `resolve event failure`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.failure(AN_EXCEPTION) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + assertThat(result).isNull() + } + + @Test + fun `resolve event null`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success(null) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + assertThat(result).isNull() + } + + @Test + fun `resolve event message text`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.MessageLike.RoomMessage( + senderId = A_USER_ID_2, + messageType = TextMessageType(body = "Hello world", formatted = null) + ) + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + val expectedResult = createNotifiableMessageEvent(body = "Hello world") + assertThat(result).isEqualTo(expectedResult) + } + + @Test + @Config(qualifiers = "en") + fun `resolve event message with mention`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.MessageLike.RoomMessage( + senderId = A_USER_ID_2, + messageType = TextMessageType(body = "Hello world", formatted = null) + ), + hasMention = true, + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + val expectedResult = createNotifiableMessageEvent(body = "Mentioned you: Hello world") + assertThat(result).isEqualTo(expectedResult) + } + + @Test + fun `resolve HTML formatted event message text takes plain text version`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.MessageLike.RoomMessage( + senderId = A_USER_ID_2, + messageType = TextMessageType( + body = "Hello world!", + formatted = FormattedBody( + body = "Hello world", + format = MessageFormat.HTML, + ) + ) + ) + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + val expectedResult = createNotifiableMessageEvent(body = "Hello world") + assertThat(result).isEqualTo(expectedResult) + } + + @Test + fun `resolve incorrectly formatted event message text uses fallback`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.MessageLike.RoomMessage( + senderId = A_USER_ID_2, + messageType = TextMessageType( + body = "Hello world", + formatted = FormattedBody( + body = "???Hello world!???", + format = MessageFormat.UNKNOWN, + ) + ) + ) + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + val expectedResult = createNotifiableMessageEvent(body = "Hello world") + assertThat(result).isEqualTo(expectedResult) + } + + @Test + fun `resolve event message audio`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.MessageLike.RoomMessage( + senderId = A_USER_ID_2, + messageType = AudioMessageType(body = "Audio", MediaSource("url"), null) + ) + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + val expectedResult = createNotifiableMessageEvent(body = "Audio") + assertThat(result).isEqualTo(expectedResult) + } + + @Test + fun `resolve event message video`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.MessageLike.RoomMessage( + senderId = A_USER_ID_2, + messageType = VideoMessageType(body = "Video", MediaSource("url"), null) + ) + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + val expectedResult = createNotifiableMessageEvent(body = "Video") + assertThat(result).isEqualTo(expectedResult) + } + + @Test + fun `resolve event message voice`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.MessageLike.RoomMessage( + senderId = A_USER_ID_2, + messageType = VoiceMessageType(body = "Voice", MediaSource("url"), null, null) + ) + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + val expectedResult = createNotifiableMessageEvent(body = "Voice message") + assertThat(result).isEqualTo(expectedResult) + } + + @Test + fun `resolve event message image`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.MessageLike.RoomMessage( + senderId = A_USER_ID_2, + messageType = ImageMessageType("Image", MediaSource("url"), null), + ) + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + val expectedResult = createNotifiableMessageEvent(body = "Image") + assertThat(result).isEqualTo(expectedResult) + } + + @Test + fun `resolve event message file`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.MessageLike.RoomMessage( + senderId = A_USER_ID_2, + messageType = FileMessageType("File", MediaSource("url"), null), + ) + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + val expectedResult = createNotifiableMessageEvent(body = "File") + assertThat(result).isEqualTo(expectedResult) + } + + @Test + fun `resolve event message location`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.MessageLike.RoomMessage( + senderId = A_USER_ID_2, + messageType = LocationMessageType("Location", "geo:1,2", null), + ) + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + val expectedResult = createNotifiableMessageEvent(body = "Location") + assertThat(result).isEqualTo(expectedResult) + } + + @Test + fun `resolve event message notice`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.MessageLike.RoomMessage( + senderId = A_USER_ID_2, + messageType = NoticeMessageType("Notice", null), + ) + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + val expectedResult = createNotifiableMessageEvent(body = "Notice") + assertThat(result).isEqualTo(expectedResult) + } + + @Test + fun `resolve event message emote`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.MessageLike.RoomMessage( + senderId = A_USER_ID_2, + messageType = EmoteMessageType("is happy", null), + ) + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + val expectedResult = createNotifiableMessageEvent(body = "* Bob is happy") + assertThat(result).isEqualTo(expectedResult) + } + + @Test + fun `resolve poll`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.MessageLike.Poll( + senderId = A_USER_ID_2, + question = "A question" + ) + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + val expectedResult = createNotifiableMessageEvent(body = "Poll: A question") + assertThat(result).isEqualTo(expectedResult) + } + + @Test + fun `resolve RoomMemberContent invite room`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.StateEvent.RoomMemberContent( + userId = A_USER_ID_2.value, + membershipState = RoomMembershipState.INVITE + ), + isDirect = false, + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + val expectedResult = InviteNotifiableEvent( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + editedEventId = null, + canBeReplaced = true, + roomName = null, + noisy = false, + title = null, + description = "Invited you to join the room", + type = null, + timestamp = A_TIMESTAMP, + soundName = null, + isRedacted = false, + isUpdated = false, + ) + assertThat(result).isEqualTo(expectedResult) + } + + @Test + fun `resolve RoomMemberContent invite direct`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.StateEvent.RoomMemberContent( + userId = A_USER_ID_2.value, + membershipState = RoomMembershipState.INVITE + ), + isDirect = true, + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + val expectedResult = InviteNotifiableEvent( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + editedEventId = null, + canBeReplaced = true, + roomName = null, + noisy = false, + title = null, + description = "Invited you to chat", + type = null, + timestamp = A_TIMESTAMP, + soundName = null, + isRedacted = false, + isUpdated = false, + ) + assertThat(result).isEqualTo(expectedResult) + } + + @Test + fun `resolve RoomMemberContent other`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.StateEvent.RoomMemberContent( + userId = A_USER_ID_2.value, + membershipState = RoomMembershipState.JOIN + ) + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + assertThat(result).isNull() + } + + @Test + fun `resolve RoomEncrypted`() = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.MessageLike.RoomEncrypted + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + val expectedResult = FallbackNotifiableEvent( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + editedEventId = null, + description = "Notification", + canBeReplaced = true, + isRedacted = false, + isUpdated = false, + timestamp = A_FAKE_TIMESTAMP, + ) + assertThat(result).isEqualTo(expectedResult) + } + + @Test + fun `resolve null cases`() { + testNull(NotificationContent.MessageLike.CallAnswer) + testNull(NotificationContent.MessageLike.CallInvite) + testNull(NotificationContent.MessageLike.CallHangup) + testNull(NotificationContent.MessageLike.CallCandidates) + testNull(NotificationContent.MessageLike.KeyVerificationReady) + testNull(NotificationContent.MessageLike.KeyVerificationStart) + testNull(NotificationContent.MessageLike.KeyVerificationCancel) + testNull(NotificationContent.MessageLike.KeyVerificationAccept) + testNull(NotificationContent.MessageLike.KeyVerificationKey) + testNull(NotificationContent.MessageLike.KeyVerificationMac) + testNull(NotificationContent.MessageLike.KeyVerificationDone) + testNull(NotificationContent.MessageLike.ReactionContent(relatedEventId = AN_EVENT_ID_2.value)) + testNull(NotificationContent.MessageLike.RoomRedaction) + testNull(NotificationContent.MessageLike.Sticker) + testNull(NotificationContent.StateEvent.PolicyRuleRoom) + testNull(NotificationContent.StateEvent.PolicyRuleServer) + testNull(NotificationContent.StateEvent.PolicyRuleUser) + testNull(NotificationContent.StateEvent.RoomAliases) + testNull(NotificationContent.StateEvent.RoomAvatar) + testNull(NotificationContent.StateEvent.RoomCanonicalAlias) + testNull(NotificationContent.StateEvent.RoomCreate) + testNull(NotificationContent.StateEvent.RoomEncryption) + testNull(NotificationContent.StateEvent.RoomGuestAccess) + testNull(NotificationContent.StateEvent.RoomHistoryVisibility) + testNull(NotificationContent.StateEvent.RoomJoinRules) + testNull(NotificationContent.StateEvent.RoomName) + testNull(NotificationContent.StateEvent.RoomPinnedEvents) + testNull(NotificationContent.StateEvent.RoomPowerLevels) + testNull(NotificationContent.StateEvent.RoomServerAcl) + testNull(NotificationContent.StateEvent.RoomThirdPartyInvite) + testNull(NotificationContent.StateEvent.RoomTombstone) + testNull(NotificationContent.StateEvent.RoomTopic) + testNull(NotificationContent.StateEvent.SpaceChild) + testNull(NotificationContent.StateEvent.SpaceParent) + } + + private fun testNull(content: NotificationContent) = runTest { + val sut = createNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = content + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + assertThat(result).isNull() + } + + private fun createNotifiableEventResolver( + notificationService: FakeNotificationService? = FakeNotificationService(), + notificationResult: Result = Result.success(null), + ): NotifiableEventResolver { + val context = RuntimeEnvironment.getApplication() as Context + notificationService?.givenGetNotificationResult(notificationResult) + val matrixClientProvider = FakeMatrixClientProvider(getClient = { + if (notificationService == null) { + Result.failure(IllegalStateException("Client not found")) + } else { + Result.success(FakeMatrixClient(notificationService = notificationService)) + } + }) + val notificationMediaRepoFactory = NotificationMediaRepo.Factory { + FakeNotificationMediaRepo() + } + return NotifiableEventResolver( + stringProvider = AndroidStringProvider(context.resources), + clock = FakeSystemClock(), + matrixClientProvider = matrixClientProvider, + notificationMediaRepoFactory = notificationMediaRepoFactory, + context = context, + ) + } + + private fun createNotificationData( + content: NotificationContent, + isDirect: Boolean = false, + hasMention: Boolean = false, + ): NotificationData { + return NotificationData( + eventId = AN_EVENT_ID, + roomId = A_ROOM_ID, + senderAvatarUrl = null, + senderDisplayName = "Bob", + roomAvatarUrl = null, + roomDisplayName = null, + isDirect = isDirect, + isEncrypted = false, + isNoisy = false, + timestamp = A_TIMESTAMP, + content = content, + hasMention = hasMention, + ) + } + + private fun createNotifiableMessageEvent(body: String): NotifiableMessageEvent { + return NotifiableMessageEvent( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + editedEventId = null, + canBeReplaced = false, + senderId = A_USER_ID_2, + noisy = false, + timestamp = A_TIMESTAMP, + senderName = "Bob", + body = body, + imageUriString = null, + threadId = null, + roomName = null, + roomIsDirect = false, + roomAvatarPath = null, + senderAvatarPath = null, + soundName = null, + outGoingMessage = false, + outGoingMessageFailed = false, + isRedacted = false, + isUpdated = false + ) + } +} + +private const val A_TIMESTAMP = 567L diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt index 18d8870ac3..ca41fcc15a 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt @@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.push.impl.notifications.fake.FakeAndroidNotificationFactory +import io.element.android.libraries.push.impl.notifications.fake.FakeImageLoader import io.element.android.libraries.push.impl.notifications.fake.FakeRoomGroupMessageCreator import io.element.android.libraries.push.impl.notifications.fake.FakeSummaryGroupMessageCreator import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent @@ -30,12 +31,15 @@ import io.element.android.libraries.push.impl.notifications.fixtures.aSimpleNoti import io.element.android.libraries.push.impl.notifications.fixtures.anInviteNotifiableEvent import kotlinx.coroutines.test.runTest import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner private val MY_AVATAR_URL: String? = null private val AN_INVITATION_EVENT = anInviteNotifiableEvent(roomId = A_ROOM_ID) private val A_SIMPLE_EVENT = aSimpleNotifiableEvent(eventId = AN_EVENT_ID) private val A_MESSAGE_EVENT = aNotifiableMessageEvent(eventId = AN_EVENT_ID, roomId = A_ROOM_ID) +@RunWith(RobolectricTestRunner::class) class NotificationFactoryTest { private val androidNotificationFactory = FakeAndroidNotificationFactory() @@ -130,11 +134,14 @@ class NotificationFactoryTest { ) val roomWithMessage = mapOf(A_ROOM_ID to listOf(ProcessedEvent(ProcessedEvent.Type.KEEP, A_MESSAGE_EVENT))) + val fakeImageLoader = FakeImageLoader() val result = roomWithMessage.toNotifications( - MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), + imageLoader = fakeImageLoader.getImageLoader(), ) assertThat(result).isEqualTo(listOf(expectedNotification)) + assertThat(fakeImageLoader.getCoilRequests().size).isEqualTo(0) } @Test @@ -142,8 +149,10 @@ class NotificationFactoryTest { val events = listOf(ProcessedEvent(ProcessedEvent.Type.REMOVE, A_MESSAGE_EVENT)) val emptyRoom = mapOf(A_ROOM_ID to events) + val fakeImageLoader = FakeImageLoader() val result = emptyRoom.toNotifications( - MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), + imageLoader = fakeImageLoader.getImageLoader(), ) assertThat(result).isEqualTo( @@ -153,14 +162,17 @@ class NotificationFactoryTest { ) ) ) + assertThat(fakeImageLoader.getCoilRequests().size).isEqualTo(0) } @Test fun `given a room with only redacted events when mapping to notification then is Empty`() = testWith(notificationFactory) { val redactedRoom = mapOf(A_ROOM_ID to listOf(ProcessedEvent(ProcessedEvent.Type.KEEP, A_MESSAGE_EVENT.copy(isRedacted = true)))) + val fakeImageLoader = FakeImageLoader() val result = redactedRoom.toNotifications( - MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), + imageLoader = fakeImageLoader.getImageLoader(), ) assertThat(result).isEqualTo( @@ -170,6 +182,7 @@ class NotificationFactoryTest { ) ) ) + assertThat(fakeImageLoader.getCoilRequests().size).isEqualTo(0) } @Test @@ -189,11 +202,14 @@ class NotificationFactoryTest { A_ROOM_ID, ) + val fakeImageLoader = FakeImageLoader() val result = roomWithRedactedMessage.toNotifications( - MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), + imageLoader = fakeImageLoader.getImageLoader(), ) assertThat(result).isEqualTo(listOf(expectedNotification)) + assertThat(fakeImageLoader.getCoilRequests().size).isEqualTo(0) } } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt index 80875406c7..18f2266b87 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt @@ -21,12 +21,15 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.push.impl.notifications.fake.FakeImageLoader import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDisplayer import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationFactory import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.mockk.mockk import kotlinx.coroutines.test.runTest import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner private const val MY_USER_DISPLAY_NAME = "display-name" private const val MY_USER_AVATAR_URL = "avatar-url" @@ -42,6 +45,7 @@ private val MESSAGE_META = RoomNotification.Message.Meta( ) private val ONE_SHOT_META = OneShotNotification.Append.Meta(key = "ignored", summaryLine = "ignored", isNoisy = false, timestamp = -1) +@RunWith(RobolectricTestRunner::class) class NotificationRendererTest { private val notificationDisplayer = FakeNotificationDisplayer() @@ -197,7 +201,8 @@ class NotificationRendererTest { notificationRenderer.render( MatrixUser(A_SESSION_ID, MY_USER_DISPLAY_NAME, MY_USER_AVATAR_URL), useCompleteNotificationFormat = USE_COMPLETE_NOTIFICATION_FORMAT, - eventsToProcess = AN_EVENT_LIST + eventsToProcess = AN_EVENT_LIST, + imageLoader = FakeImageLoader().getImageLoader(), ) } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreatorTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreatorTest.kt new file mode 100644 index 0000000000..399b0fc4b3 --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreatorTest.kt @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.push.impl.notifications + +import android.content.Context +import android.os.Build +import coil.annotation.ExperimentalCoilApi +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.ui.components.aMatrixUser +import io.element.android.libraries.matrix.ui.media.MediaRequestData +import io.element.android.libraries.push.impl.notifications.factories.createNotificationCreator +import io.element.android.libraries.push.impl.notifications.fake.FakeImageLoader +import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent +import io.element.android.services.toolbox.api.sdk.BuildVersionSdkIntProvider +import io.element.android.services.toolbox.impl.strings.AndroidStringProvider +import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvider +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +private const val A_TIMESTAMP = 6480L +private const val A_ROOM_AVATAR = "mxc://roomAvatar" +private const val A_USER_AVATAR_1 = "mxc://userAvatar1" +private const val A_USER_AVATAR_2 = "mxc://userAvatar2" + +@RunWith(RobolectricTestRunner::class) +class RoomGroupMessageCreatorTest { + @Test + fun `test createRoomMessage with one Event`() = runTest { + val sut = createRoomGroupMessageCreator() + val fakeImageLoader = FakeImageLoader() + val result = sut.createRoomMessage( + currentUser = aMatrixUser(), + events = listOf( + aNotifiableMessageEvent(timestamp = A_TIMESTAMP).copy( + imageUriString = "aUri", + ) + ), + roomId = A_ROOM_ID, + imageLoader = fakeImageLoader.getImageLoader(), + ) + val resultMetaWithoutFormatting = result.meta.copy( + summaryLine = result.meta.summaryLine.toString() + ) + assertThat(resultMetaWithoutFormatting).isEqualTo( + RoomNotification.Message.Meta( + roomId = A_ROOM_ID, + summaryLine = "room-name: sender-name message-body", + messageCount = 1, + latestTimestamp = A_TIMESTAMP, + shouldBing = false, + ) + ) + assertThat(fakeImageLoader.getCoilRequests().size).isEqualTo(0) + } + + @Test + fun `test createRoomMessage with one noisy Event`() = runTest { + val sut = createRoomGroupMessageCreator() + val fakeImageLoader = FakeImageLoader() + val result = sut.createRoomMessage( + currentUser = aMatrixUser(), + events = listOf( + aNotifiableMessageEvent(timestamp = A_TIMESTAMP).copy( + noisy = true, + ) + ), + roomId = A_ROOM_ID, + imageLoader = fakeImageLoader.getImageLoader(), + ) + val resultMetaWithoutFormatting = result.meta.copy( + summaryLine = result.meta.summaryLine.toString() + ) + assertThat(resultMetaWithoutFormatting).isEqualTo( + RoomNotification.Message.Meta( + roomId = A_ROOM_ID, + summaryLine = "room-name: sender-name message-body", + messageCount = 1, + latestTimestamp = A_TIMESTAMP, + shouldBing = true, + ) + ) + assertThat(fakeImageLoader.getCoilRequests().size).isEqualTo(0) + } + + @Test + fun `test createRoomMessage with room avatar and sender avatar android O`() { + `test createRoomMessage with room avatar and sender avatar`( + api = Build.VERSION_CODES.O, + // Only the Room avatar is loaded + expectedCoilRequests = listOf( + MediaRequestData( + source = MediaSource(url = A_ROOM_AVATAR), + kind = MediaRequestData.Kind.Thumbnail(1024) + ) + ) + ) + } + + @OptIn(ExperimentalCoilApi::class) + @Test + fun `test createRoomMessage with room avatar and sender avatar android P`() = runTest { + `test createRoomMessage with room avatar and sender avatar`( + api = Build.VERSION_CODES.P, + // Room and user avatar are loaded + expectedCoilRequests = listOf( + MediaRequestData( + source = MediaSource(url = A_USER_AVATAR_1), + kind = MediaRequestData.Kind.Thumbnail(1024) + ), + MediaRequestData( + source = MediaSource(url = A_USER_AVATAR_2), + kind = MediaRequestData.Kind.Thumbnail(1024) + ), + MediaRequestData( + source = MediaSource(url = A_ROOM_AVATAR), + kind = MediaRequestData.Kind.Thumbnail(1024) + ), + ) + ) + } + + @OptIn(ExperimentalCoilApi::class) + private fun `test createRoomMessage with room avatar and sender avatar`( + api: Int, + expectedCoilRequests: List, + ) = runTest { + val fakeImageLoader = FakeImageLoader() + val sut = createRoomGroupMessageCreator( + sdkIntProvider = FakeBuildVersionSdkIntProvider(api) + ) + val result = sut.createRoomMessage( + currentUser = aMatrixUser( + // Some user avatar + avatarUrl = A_USER_AVATAR_1, + ), + events = listOf( + aNotifiableMessageEvent(timestamp = A_TIMESTAMP).copy( + roomAvatarPath = A_ROOM_AVATAR, + senderAvatarPath = A_USER_AVATAR_2, + ) + ), + roomId = A_ROOM_ID, + imageLoader = fakeImageLoader.getImageLoader(), + ) + val resultMetaWithoutFormatting = result.meta.copy( + summaryLine = result.meta.summaryLine.toString() + ) + assertThat(resultMetaWithoutFormatting).isEqualTo( + RoomNotification.Message.Meta( + roomId = A_ROOM_ID, + summaryLine = "room-name: sender-name message-body", + messageCount = 1, + latestTimestamp = A_TIMESTAMP, + shouldBing = false, + ) + ) + assertThat(fakeImageLoader.getCoilRequests()).isEqualTo(expectedCoilRequests) + } + + @Test + fun `test createRoomMessage with two Events`() = runTest { + val sut = createRoomGroupMessageCreator() + val fakeImageLoader = FakeImageLoader() + val result = sut.createRoomMessage( + currentUser = aMatrixUser(), + events = listOf( + aNotifiableMessageEvent(timestamp = A_TIMESTAMP), + aNotifiableMessageEvent(timestamp = A_TIMESTAMP + 10), + ), + roomId = A_ROOM_ID, + imageLoader = fakeImageLoader.getImageLoader(), + ) + val resultMetaWithoutFormatting = result.meta.copy( + summaryLine = result.meta.summaryLine.toString() + ) + assertThat(resultMetaWithoutFormatting).isEqualTo( + RoomNotification.Message.Meta( + roomId = A_ROOM_ID, + summaryLine = "room-name: 2 messages", + messageCount = 2, + latestTimestamp = A_TIMESTAMP + 10, + shouldBing = false, + ) + ) + assertThat(fakeImageLoader.getCoilRequests().size).isEqualTo(0) + } + + @Test + fun `test createRoomMessage with smart reply error`() = runTest { + val sut = createRoomGroupMessageCreator() + val fakeImageLoader = FakeImageLoader() + val result = sut.createRoomMessage( + currentUser = aMatrixUser(), + events = listOf( + aNotifiableMessageEvent(timestamp = A_TIMESTAMP).copy( + outGoingMessage = true, + outGoingMessageFailed = true, + ), + ), + roomId = A_ROOM_ID, + imageLoader = fakeImageLoader.getImageLoader(), + ) + val resultMetaWithoutFormatting = result.meta.copy( + summaryLine = result.meta.summaryLine.toString() + ) + assertThat(resultMetaWithoutFormatting).isEqualTo( + RoomNotification.Message.Meta( + roomId = A_ROOM_ID, + summaryLine = "room-name: sender-name message-body", + messageCount = 0, + latestTimestamp = A_TIMESTAMP, + shouldBing = false, + ) + ) + assertThat(fakeImageLoader.getCoilRequests().size).isEqualTo(0) + } + + @Test + fun `test createRoomMessage for direct room`() = runTest { + val sut = createRoomGroupMessageCreator() + val fakeImageLoader = FakeImageLoader() + val result = sut.createRoomMessage( + currentUser = aMatrixUser(), + events = listOf( + aNotifiableMessageEvent(timestamp = A_TIMESTAMP).copy( + roomIsDirect = true, + ), + ), + roomId = A_ROOM_ID, + imageLoader = fakeImageLoader.getImageLoader(), + ) + val resultMetaWithoutFormatting = result.meta.copy( + summaryLine = result.meta.summaryLine.toString() + ) + assertThat(resultMetaWithoutFormatting).isEqualTo( + RoomNotification.Message.Meta( + roomId = A_ROOM_ID, + summaryLine = "sender-name: message-body", + messageCount = 1, + latestTimestamp = A_TIMESTAMP, + shouldBing = false, + ) + ) + assertThat(fakeImageLoader.getCoilRequests().size).isEqualTo(0) + } +} + +fun createRoomGroupMessageCreator( + sdkIntProvider: BuildVersionSdkIntProvider = FakeBuildVersionSdkIntProvider(Build.VERSION_CODES.O), +): RoomGroupMessageCreator { + val context = RuntimeEnvironment.getApplication() as Context + return RoomGroupMessageCreator( + notificationCreator = createNotificationCreator(), + bitmapLoader = NotificationBitmapLoader( + context = RuntimeEnvironment.getApplication(), + sdkIntProvider = sdkIntProvider, + ), + stringProvider = AndroidStringProvider(context.resources) + ) +} diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/FakeIntentProvider.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/FakeIntentProvider.kt new file mode 100644 index 0000000000..2de70bb672 --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/FakeIntentProvider.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.push.impl.notifications.factories + +import android.content.Intent +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.core.ThreadId +import io.element.android.libraries.push.impl.intent.IntentProvider + +class FakeIntentProvider : IntentProvider { + override fun getViewRoomIntent(sessionId: SessionId, roomId: RoomId?, threadId: ThreadId?) = Intent() + + override fun getInviteListIntent(sessionId: SessionId) = Intent() +} diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreatorTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreatorTest.kt new file mode 100644 index 0000000000..edf87fcd1a --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreatorTest.kt @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.push.impl.notifications.factories + +import android.app.Notification +import android.content.Context +import androidx.core.app.NotificationCompat +import androidx.core.app.Person +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.matrix.test.A_THREAD_ID +import io.element.android.libraries.matrix.test.core.aBuildMeta +import io.element.android.libraries.matrix.ui.components.aMatrixUser +import io.element.android.libraries.push.impl.notifications.NotificationActionIds +import io.element.android.libraries.push.impl.notifications.RoomEventGroupInfo +import io.element.android.libraries.push.impl.notifications.channels.NotificationChannels +import io.element.android.libraries.push.impl.notifications.factories.action.MarkAsReadActionFactory +import io.element.android.libraries.push.impl.notifications.factories.action.QuickReplyActionFactory +import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent +import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent +import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent +import io.element.android.services.toolbox.test.strings.FakeStringProvider +import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP +import io.element.android.services.toolbox.test.systemclock.FakeSystemClock +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +@RunWith(RobolectricTestRunner::class) +class NotificationCreatorTest { + @Test + fun `test createDiagnosticNotification`() { + val sut = createNotificationCreator() + val result = sut.createDiagnosticNotification() + result.commonAssertions( + expectedGroup = null, + expectedCategory = NotificationCompat.CATEGORY_STATUS, + ) + } + + @Test + fun `test createFallbackNotification`() { + val sut = createNotificationCreator() + val result = sut.createFallbackNotification( + FallbackNotifiableEvent( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + editedEventId = null, + description = "description", + canBeReplaced = false, + isRedacted = false, + isUpdated = false, + timestamp = A_FAKE_TIMESTAMP, + ) + ) + result.commonAssertions( + expectedCategory = null, + ) + } + + @Test + fun `test createSimpleEventNotification`() { + val sut = createNotificationCreator() + val result = sut.createSimpleEventNotification( + SimpleNotifiableEvent( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + editedEventId = null, + noisy = false, + title = "title", + description = "description", + type = null, + timestamp = A_FAKE_TIMESTAMP, + soundName = null, + canBeReplaced = false, + isRedacted = false, + isUpdated = false, + ) + ) + result.commonAssertions( + expectedCategory = null, + ) + } + + @Test + fun `test createSimpleEventNotification noisy`() { + val sut = createNotificationCreator() + val result = sut.createSimpleEventNotification( + SimpleNotifiableEvent( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + editedEventId = null, + noisy = true, + title = "title", + description = "description", + type = null, + timestamp = A_FAKE_TIMESTAMP, + soundName = null, + canBeReplaced = false, + isRedacted = false, + isUpdated = false, + ) + ) + result.commonAssertions( + expectedCategory = null, + ) + } + + @Test + fun `test createRoomInvitationNotification`() { + val sut = createNotificationCreator() + val result = sut.createRoomInvitationNotification( + InviteNotifiableEvent( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + editedEventId = null, + noisy = false, + title = "title", + description = "description", + type = null, + timestamp = A_FAKE_TIMESTAMP, + soundName = null, + canBeReplaced = false, + isRedacted = false, + isUpdated = false, + roomName = "roomName", + ) + ) + result.commonAssertions( + expectedCategory = null, + ) + } + + @Test + fun `test createRoomInvitationNotification noisy`() { + val sut = createNotificationCreator() + val result = sut.createRoomInvitationNotification( + InviteNotifiableEvent( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + editedEventId = null, + noisy = true, + title = "title", + description = "description", + type = null, + timestamp = A_FAKE_TIMESTAMP, + soundName = null, + canBeReplaced = false, + isRedacted = false, + isUpdated = false, + roomName = "roomName", + ) + ) + result.commonAssertions( + expectedCategory = null, + ) + } + + @Test + fun `test createSummaryListNotification`() { + val sut = createNotificationCreator() + val matrixUser = aMatrixUser() + val result = sut.createSummaryListNotification( + currentUser = matrixUser, + style = null, + compatSummary = "compatSummary", + noisy = false, + lastMessageTimestamp = 123_456L, + ) + result.commonAssertions( + expectedGroup = matrixUser.userId.value, + ) + } + + @Test + fun `test createSummaryListNotification noisy`() { + val sut = createNotificationCreator() + val matrixUser = aMatrixUser() + val result = sut.createSummaryListNotification( + currentUser = matrixUser, + style = null, + compatSummary = "compatSummary", + noisy = true, + lastMessageTimestamp = 123_456L, + ) + result.commonAssertions( + expectedGroup = matrixUser.userId.value, + ) + } + + @Test + fun `test createMessagesListNotification`() { + val sut = createNotificationCreator() + aMatrixUser() + val result = sut.createMessagesListNotification( + messageStyle = NotificationCompat.MessagingStyle( + Person.Builder() + .setName("name") + .build() + ), + roomInfo = RoomEventGroupInfo( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + roomDisplayName = "roomDisplayName", + isDirect = false, + hasSmartReplyError = false, + shouldBing = false, + customSound = null, + isUpdated = false, + ), + threadId = null, + largeIcon = null, + lastMessageTimestamp = 123_456L, + tickerText = "tickerText", + ) + result.commonAssertions() + } + + @Test + fun `test createMessagesListNotification should bing and thread`() { + val sut = createNotificationCreator() + aMatrixUser() + val result = sut.createMessagesListNotification( + messageStyle = NotificationCompat.MessagingStyle( + Person.Builder() + .setName("name") + .build() + ), + roomInfo = RoomEventGroupInfo( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + roomDisplayName = "roomDisplayName", + isDirect = false, + hasSmartReplyError = false, + shouldBing = true, + customSound = null, + isUpdated = false, + ), + threadId = A_THREAD_ID, + largeIcon = null, + lastMessageTimestamp = 123_456L, + tickerText = "tickerText", + ) + result.commonAssertions() + } + + private fun Notification.commonAssertions( + expectedGroup: String? = A_SESSION_ID.value, + expectedCategory: String? = NotificationCompat.CATEGORY_MESSAGE, + ) { + assertThat(contentIntent).isNotNull() + assertThat(group).isEqualTo(expectedGroup) + assertThat(category).isEqualTo(expectedCategory) + } +} + +fun createNotificationCreator( + context: Context = RuntimeEnvironment.getApplication(), + buildMeta: BuildMeta = aBuildMeta(), + notificationChannels: NotificationChannels = createNotificationChannels() +): NotificationCreator { + return NotificationCreator( + context = context, + notificationChannels = notificationChannels, + stringProvider = FakeStringProvider("test"), + buildMeta = buildMeta, + pendingIntentFactory = PendingIntentFactory( + context, + FakeIntentProvider(), + FakeSystemClock(), + NotificationActionIds(buildMeta), + ), + markAsReadActionFactory = MarkAsReadActionFactory( + context = context, + actionIds = NotificationActionIds(buildMeta), + stringProvider = FakeStringProvider("MarkAsReadActionFactory"), + clock = FakeSystemClock(), + ), + quickReplyActionFactory = QuickReplyActionFactory( + context = context, + actionIds = NotificationActionIds(buildMeta), + stringProvider = FakeStringProvider("QuickReplyActionFactory"), + clock = FakeSystemClock(), + ), + ) +} + +fun createNotificationChannels(): NotificationChannels { + val context = RuntimeEnvironment.getApplication() + return NotificationChannels(context, FakeStringProvider("")) +} diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeAndroidNotificationFactory.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeAndroidNotificationFactory.kt index c046e1253f..6637b64979 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeAndroidNotificationFactory.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeAndroidNotificationFactory.kt @@ -17,14 +17,14 @@ package io.element.android.libraries.push.impl.notifications.fake import android.app.Notification -import io.element.android.libraries.push.impl.notifications.factories.NotificationFactory +import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent import io.mockk.every import io.mockk.mockk class FakeAndroidNotificationFactory { - val instance = mockk() + val instance = mockk() fun givenCreateRoomInvitationNotificationFor(event: InviteNotifiableEvent): Notification { val mockNotification = mockk() diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeImageLoader.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeImageLoader.kt new file mode 100644 index 0000000000..5c747b45e2 --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeImageLoader.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.push.impl.notifications.fake + +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import coil.ImageLoader +import coil.annotation.ExperimentalCoilApi +import coil.test.FakeImageLoaderEngine +import org.robolectric.RuntimeEnvironment + +@OptIn(ExperimentalCoilApi::class) +class FakeImageLoader { + private val coilRequests = mutableListOf() + + private var cache: ImageLoader? = null + + fun getImageLoader(): ImageLoader { + return cache ?: ImageLoader.Builder(RuntimeEnvironment.getApplication()) + .components { + val engine = FakeImageLoaderEngine.Builder() + .intercept( + predicate = { + coilRequests.add(it) + true + }, + drawable = ColorDrawable(Color.BLUE) + ) + .build() + add(engine) + } + .build() + .also { + cache = it + } + } + + fun getCoilRequests(): List { + return coilRequests.toList() + } +} diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeImageLoaderHolder.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeImageLoaderHolder.kt new file mode 100644 index 0000000000..0e18036e94 --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeImageLoaderHolder.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.push.impl.notifications.fake + +import coil.ImageLoader +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder + +class FakeImageLoaderHolder : ImageLoaderHolder { + private val fakeImageLoader = FakeImageLoader() + override fun get(client: MatrixClient): ImageLoader { + return fakeImageLoader.getImageLoader() + } +} diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt index 60b9e10c3d..9c7755aa9d 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt @@ -40,7 +40,7 @@ class FakeNotificationFactory { summaryNotification: SummaryNotification ) { with(instance) { - coEvery { groupedEvents.roomEvents.toNotifications(matrixUser) } returns roomNotifications + coEvery { groupedEvents.roomEvents.toNotifications(matrixUser, any()) } returns roomNotifications every { groupedEvents.invitationEvents.toNotifications() } returns invitationNotifications every { groupedEvents.simpleEvents.toNotifications() } returns simpleNotifications every { groupedEvents.fallbackEvents.toNotifications() } returns fallbackNotifications diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationMediaRepo.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationMediaRepo.kt new file mode 100644 index 0000000000..2657d984a0 --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationMediaRepo.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.push.impl.notifications.fake + +import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.push.impl.notifications.NotificationMediaRepo +import java.io.File + +class FakeNotificationMediaRepo : NotificationMediaRepo { + override suspend fun getMediaFile( + mediaSource: MediaSource, + mimeType: String?, + body: String?, + ): Result { + return Result.failure(IllegalStateException("Fake class")) + } +} diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt index b896737e6f..946b0ab0db 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt @@ -39,6 +39,7 @@ class FakeRoomGroupMessageCreator { currentUser = matrixUser, events = events, roomId = roomId, + imageLoader = any(), ) } returns mockMessage return mockMessage diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt index 780d2abb71..44a2873465 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt @@ -77,13 +77,14 @@ fun aNotifiableMessageEvent( roomId: RoomId = A_ROOM_ID, eventId: EventId = AN_EVENT_ID, threadId: ThreadId? = null, - isRedacted: Boolean = false + isRedacted: Boolean = false, + timestamp: Long = 0, ) = NotifiableMessageEvent( sessionId = sessionId, eventId = eventId, editedEventId = null, noisy = false, - timestamp = 0, + timestamp = timestamp, senderName = "sender-name", senderId = UserId("@sending-id:domain.com"), body = "message-body", diff --git a/libraries/roomselect/api/build.gradle.kts b/libraries/roomselect/api/build.gradle.kts new file mode 100644 index 0000000000..fa19346197 --- /dev/null +++ b/libraries/roomselect/api/build.gradle.kts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 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. + */ +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.libraries.roomselect.api" +} + +dependencies { + implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) +} diff --git a/libraries/roomselect/api/src/main/kotlin/io/element/android/libraries/roomselect/api/RoomSelectEntryPoint.kt b/libraries/roomselect/api/src/main/kotlin/io/element/android/libraries/roomselect/api/RoomSelectEntryPoint.kt new file mode 100644 index 0000000000..873d6f4a9b --- /dev/null +++ b/libraries/roomselect/api/src/main/kotlin/io/element/android/libraries/roomselect/api/RoomSelectEntryPoint.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.roomselect.api + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import io.element.android.libraries.architecture.FeatureEntryPoint +import io.element.android.libraries.matrix.api.core.RoomId + +interface RoomSelectEntryPoint : FeatureEntryPoint { + data class Params( + val mode: RoomSelectMode, + ) + + fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder + interface NodeBuilder { + fun params(params: Params): NodeBuilder + fun callback(callback: Callback): NodeBuilder + fun build(): Node + } + + interface Callback : Plugin { + fun onRoomSelected(roomIds: List) + fun onCancel() + } +} + diff --git a/libraries/roomselect/api/src/main/kotlin/io/element/android/libraries/roomselect/api/RoomSelectMode.kt b/libraries/roomselect/api/src/main/kotlin/io/element/android/libraries/roomselect/api/RoomSelectMode.kt new file mode 100644 index 0000000000..d3f63e366c --- /dev/null +++ b/libraries/roomselect/api/src/main/kotlin/io/element/android/libraries/roomselect/api/RoomSelectMode.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.roomselect.api + +enum class RoomSelectMode { + Forward, +} diff --git a/libraries/roomselect/impl/build.gradle.kts b/libraries/roomselect/impl/build.gradle.kts new file mode 100644 index 0000000000..2eaf5c3db7 --- /dev/null +++ b/libraries/roomselect/impl/build.gradle.kts @@ -0,0 +1,52 @@ +/* + * 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. + */ + +plugins { + id("io.element.android-compose-library") + alias(libs.plugins.anvil) + alias(libs.plugins.ksp) + id("kotlin-parcelize") +} + +android { + namespace = "io.element.android.libraries.roomselect.impl" +} + +anvil { + generateDaggerFactories.set(true) +} + +dependencies { + implementation(projects.anvilannotations) + anvil(projects.anvilcodegen) + implementation(projects.libraries.core) + implementation(projects.libraries.androidutils) + implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) + implementation(projects.libraries.matrixui) + implementation(projects.libraries.designsystem) + implementation(projects.libraries.uiStrings) + api(projects.libraries.roomselect.api) + ksp(libs.showkase.processor) + + testImplementation(libs.test.junit) + testImplementation(libs.coroutines.test) + testImplementation(libs.molecule.runtime) + testImplementation(libs.test.truth) + testImplementation(libs.test.turbine) + testImplementation(projects.libraries.matrix.test) + testImplementation(projects.tests.testutils) +} diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPoint.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPoint.kt new file mode 100644 index 0000000000..d3700c96f5 --- /dev/null +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPoint.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.roomselect.impl + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.roomselect.api.RoomSelectEntryPoint +import javax.inject.Inject + +@ContributesBinding(SessionScope::class) +class DefaultRoomSelectEntryPoint @Inject constructor() : RoomSelectEntryPoint { + override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): RoomSelectEntryPoint.NodeBuilder { + val plugins = ArrayList() + + return object : RoomSelectEntryPoint.NodeBuilder { + override fun params(params: RoomSelectEntryPoint.Params): RoomSelectEntryPoint.NodeBuilder { + plugins += RoomSelectNode.Inputs(mode = params.mode) + return this + } + + override fun callback(callback: RoomSelectEntryPoint.Callback): RoomSelectEntryPoint.NodeBuilder { + plugins += callback + return this + } + + override fun build(): Node { + return parentNode.createNode(buildContext, plugins) + } + } + } +} + diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimpleRoomList.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectEvents.kt similarity index 62% rename from libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimpleRoomList.kt rename to libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectEvents.kt index 28b04ae318..76dce0dd5d 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimpleRoomList.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectEvents.kt @@ -14,13 +14,15 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.test.roomlist +package io.element.android.libraries.roomselect.impl -import io.element.android.libraries.matrix.api.roomlist.RoomList -import io.element.android.libraries.matrix.api.roomlist.RoomSummary -import kotlinx.coroutines.flow.StateFlow +import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails -data class SimpleRoomList( - override val summaries: StateFlow>, - override val loadingState: StateFlow -) : RoomList +sealed interface RoomSelectEvents { + data class SetSelectedRoom(val room: RoomSummaryDetails) : RoomSelectEvents + + // TODO remove to restore multi-selection + data object RemoveSelectedRoom : RoomSelectEvents + data object ToggleSearchActive : RoomSelectEvents + data class UpdateQuery(val query: String) : RoomSelectEvents +} diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt new file mode 100644 index 0000000000..6b7b8054b2 --- /dev/null +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.roomselect.impl + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import io.element.android.anvilannotations.ContributesNode +import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.inputs +import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.roomselect.api.RoomSelectEntryPoint +import io.element.android.libraries.roomselect.api.RoomSelectMode + +@ContributesNode(SessionScope::class) +class RoomSelectNode @AssistedInject constructor( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, + presenterFactory: RoomSelectPresenter.Factory, +) : Node(buildContext, plugins = plugins) { + data class Inputs( + val mode: RoomSelectMode, + ) : NodeInputs + + private val inputs: Inputs = inputs() + private val presenter = presenterFactory.create(inputs.mode) + + private val callbacks = plugins.filterIsInstance() + + private fun onDismiss() { + callbacks.forEach { it.onCancel() } + } + + private fun onSubmit(roomIds: List) { + callbacks.forEach { it.onRoomSelected(roomIds) } + } + + @Composable + override fun View(modifier: Modifier) { + val state = presenter.present() + RoomSelectView( + state = state, + onDismiss = ::onDismiss, + onSubmit = ::onSubmit, + modifier = modifier + ) + } +} diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt new file mode 100644 index 0000000000..16cd7b813d --- /dev/null +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.roomselect.impl + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import io.element.android.libraries.roomselect.api.RoomSelectMode +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.designsystem.theme.components.SearchBarResultState +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.roomlist.RoomSummary +import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList + +class RoomSelectPresenter @AssistedInject constructor( + @Assisted private val mode: RoomSelectMode, + private val client: MatrixClient, +) : Presenter { + + @AssistedFactory + interface Factory { + fun create(mode: RoomSelectMode): RoomSelectPresenter + } + + @Composable + override fun present(): RoomSelectState { + var selectedRooms by remember { mutableStateOf(persistentListOf()) } + var query by remember { mutableStateOf("") } + var isSearchActive by remember { mutableStateOf(false) } + var results: SearchBarResultState> by remember { mutableStateOf(SearchBarResultState.NotSearching()) } + + val summaries by client.roomListService.allRooms.summaries.collectAsState() + + LaunchedEffect(query, summaries) { + val filteredSummaries = summaries.filterIsInstance() + .map { it.details } + .filter { it.name.contains(query, ignoreCase = true) } + .distinctBy { it.roomId } // This should be removed once we're sure no duplicate Rooms can be received + .toPersistentList() + results = if (filteredSummaries.isNotEmpty()) { + SearchBarResultState.Results(filteredSummaries) + } else { + SearchBarResultState.NoResults() + } + } + + fun handleEvents(event: RoomSelectEvents) { + when (event) { + is RoomSelectEvents.SetSelectedRoom -> { + selectedRooms = persistentListOf(event.room) + // Restore for multi-selection +// val index = selectedRooms.indexOfFirst { it.roomId == event.room.roomId } +// selectedRooms = if (index >= 0) { +// selectedRooms.removeAt(index) +// } else { +// selectedRooms.add(event.room) +// } + } + RoomSelectEvents.RemoveSelectedRoom -> selectedRooms = persistentListOf() + is RoomSelectEvents.UpdateQuery -> query = event.query + RoomSelectEvents.ToggleSearchActive -> isSearchActive = !isSearchActive + } + } + + return RoomSelectState( + mode = mode, + resultState = results, + query = query, + isSearchActive = isSearchActive, + selectedRooms = selectedRooms, + eventSink = { handleEvents(it) } + ) + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomList.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectState.kt similarity index 54% rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomList.kt rename to libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectState.kt index 481b38dd9b..20fa7d910d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomList.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectState.kt @@ -14,16 +14,18 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.impl.roomlist +package io.element.android.libraries.roomselect.impl -import io.element.android.libraries.matrix.api.roomlist.RoomList -import io.element.android.libraries.matrix.api.roomlist.RoomSummary -import kotlinx.coroutines.flow.StateFlow +import io.element.android.libraries.roomselect.api.RoomSelectMode +import io.element.android.libraries.designsystem.theme.components.SearchBarResultState +import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails +import kotlinx.collections.immutable.ImmutableList -/** - * Simple implementation of [RoomList] where state flows are provided through constructor. - */ -class RustRoomList( - override val summaries: StateFlow>, - override val loadingState: StateFlow -) : RoomList +data class RoomSelectState( + val mode: RoomSelectMode, + val resultState: SearchBarResultState>, + val query: String, + val isSearchActive: Boolean, + val selectedRooms: ImmutableList, + val eventSink: (RoomSelectEvents) -> Unit +) diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt new file mode 100644 index 0000000000..d06e0be6d6 --- /dev/null +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.roomselect.impl + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.designsystem.theme.components.SearchBarResultState +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.RoomMember +import io.element.android.libraries.matrix.api.room.message.RoomMessage +import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails +import io.element.android.libraries.roomselect.api.RoomSelectMode +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf + +open class RoomSelectStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aRoomSelectState(), + aRoomSelectState(query = "Test", isSearchActive = true), + aRoomSelectState(resultState = SearchBarResultState.Results(aForwardMessagesRoomList())), + aRoomSelectState( + resultState = SearchBarResultState.Results(aForwardMessagesRoomList()), + query = "Test", + isSearchActive = true, + ), + aRoomSelectState( + resultState = SearchBarResultState.Results(aForwardMessagesRoomList()), + query = "Test", + isSearchActive = true, + selectedRooms = persistentListOf(aRoomDetailsState(roomId = RoomId("!room2:domain"))) + ), + // Add other states here + ) +} + +private fun aRoomSelectState( + resultState: SearchBarResultState> = SearchBarResultState.NotSearching(), + query: String = "", + isSearchActive: Boolean = false, + selectedRooms: ImmutableList = persistentListOf(), +) = RoomSelectState( + mode = RoomSelectMode.Forward, + resultState = resultState, + query = query, + isSearchActive = isSearchActive, + selectedRooms = selectedRooms, + eventSink = {} +) + +private fun aForwardMessagesRoomList() = persistentListOf( + aRoomDetailsState(), + aRoomDetailsState( + roomId = RoomId("!room2:domain"), + name = "Room with alias", + canonicalAlias = "#alias:example.org", + ), +) + +private fun aRoomDetailsState( + roomId: RoomId = RoomId("!room:domain"), + name: String = "roomName", + canonicalAlias: String? = null, + isDirect: Boolean = true, + avatarURLString: String? = null, + lastMessage: RoomMessage? = null, + lastMessageTimestamp: Long? = null, + unreadNotificationCount: Int = 0, + inviter: RoomMember? = null, +) = RoomSummaryDetails( + roomId = roomId, + name = name, + canonicalAlias = canonicalAlias, + isDirect = isDirect, + avatarURLString = avatarURLString, + lastMessage = lastMessage, + lastMessageTimestamp = lastMessageTimestamp, + unreadNotificationCount = unreadNotificationCount, + inviter = inviter, +) diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt new file mode 100644 index 0000000000..264be0cca7 --- /dev/null +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.roomselect.impl + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.designsystem.components.avatar.Avatar +import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.aliasScreenTitle +import io.element.android.libraries.designsystem.theme.components.HorizontalDivider +import io.element.android.libraries.designsystem.theme.components.RadioButton +import io.element.android.libraries.designsystem.theme.components.Scaffold +import io.element.android.libraries.designsystem.theme.components.SearchBar +import io.element.android.libraries.designsystem.theme.components.SearchBarResultState +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.theme.components.TextButton +import io.element.android.libraries.designsystem.theme.components.TopAppBar +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails +import io.element.android.libraries.matrix.ui.components.SelectedRoom +import io.element.android.libraries.roomselect.api.RoomSelectMode +import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.ImmutableList + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun RoomSelectView( + state: RoomSelectState, + onDismiss: () -> Unit, + onSubmit: (List) -> Unit, + modifier: Modifier = Modifier, +) { + @Suppress("UNUSED_PARAMETER") + fun onRoomRemoved(roomSummaryDetails: RoomSummaryDetails) { + // TODO toggle selection when multi-selection is enabled + state.eventSink(RoomSelectEvents.RemoveSelectedRoom) + } + + @Composable + fun SelectedRoomsHelper(isForwarding: Boolean, selectedRooms: ImmutableList) { + if (isForwarding) return + SelectedRooms( + selectedRooms = selectedRooms, + onRoomRemoved = ::onRoomRemoved, + modifier = Modifier.padding(vertical = 16.dp) + ) + } + + fun onBackButton(state: RoomSelectState) { + if (state.isSearchActive) { + state.eventSink(RoomSelectEvents.ToggleSearchActive) + } else { + onDismiss() + } + } + + BackHandler(onBack = { onBackButton(state) }) + + Scaffold( + modifier = modifier, + topBar = { + TopAppBar( + title = { + Text( + text = when (state.mode) { + RoomSelectMode.Forward -> stringResource(CommonStrings.common_forward_message) + }, + style = ElementTheme.typography.aliasScreenTitle + ) + }, + navigationIcon = { + BackButton(onClick = { onBackButton(state) }) + }, + actions = { + TextButton( + text = stringResource(CommonStrings.action_send), + enabled = state.selectedRooms.isNotEmpty(), + onClick = { onSubmit(state.selectedRooms.map { it.roomId }) } + ) + } + ) + } + ) { paddingValues -> + Column( + Modifier + .padding(paddingValues) + .consumeWindowInsets(paddingValues) + ) { + SearchBar( + placeHolderTitle = stringResource(CommonStrings.action_search), + query = state.query, + onQueryChange = { state.eventSink(RoomSelectEvents.UpdateQuery(it)) }, + active = state.isSearchActive, + onActiveChange = { state.eventSink(RoomSelectEvents.ToggleSearchActive) }, + resultState = state.resultState, + showBackButton = false, + ) { summaries -> + LazyColumn { + item { + SelectedRoomsHelper( + isForwarding = false, // TODO state.isForwarding, + selectedRooms = state.selectedRooms + ) + } + items(summaries, key = { it.roomId.value }) { roomSummary -> + Column { + RoomSummaryView( + roomSummary, + isSelected = state.selectedRooms.any { it.roomId == roomSummary.roomId }, + onSelection = { roomSummary -> + state.eventSink(RoomSelectEvents.SetSelectedRoom(roomSummary)) + } + ) + HorizontalDivider(modifier = Modifier.fillMaxWidth()) + } + } + } + } + + if (!state.isSearchActive) { + // TODO restore for multi-selection +// SelectedRoomsHelper( +// isForwarding = state.isForwarding, +// selectedRooms = state.selectedRooms +// ) + Spacer(modifier = Modifier.height(20.dp)) + + if (state.resultState is SearchBarResultState.Results) { + LazyColumn { + items(state.resultState.results, key = { it.roomId.value }) { roomSummary -> + Column { + RoomSummaryView( + roomSummary, + isSelected = state.selectedRooms.any { it.roomId == roomSummary.roomId }, + onSelection = { roomSummary -> + state.eventSink(RoomSelectEvents.SetSelectedRoom(roomSummary)) + } + ) + HorizontalDivider(modifier = Modifier.fillMaxWidth()) + } + } + } + } + } + } + } +} + +@Composable +private fun SelectedRooms( + selectedRooms: ImmutableList, + onRoomRemoved: (RoomSummaryDetails) -> Unit, + modifier: Modifier = Modifier, +) { + LazyRow( + modifier, + contentPadding = PaddingValues(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(32.dp) + ) { + items(selectedRooms, key = { it.roomId.value }) { roomSummary -> + SelectedRoom(roomSummary = roomSummary, onRoomRemoved = onRoomRemoved) + } + } +} + +@Composable +private fun RoomSummaryView( + summary: RoomSummaryDetails, + isSelected: Boolean, + onSelection: (RoomSummaryDetails) -> Unit, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier + .clickable { onSelection(summary) } + .fillMaxWidth() + .padding(start = 16.dp, end = 4.dp) + .heightIn(56.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Avatar( + avatarData = AvatarData( + id = summary.roomId.value, + name = summary.name, + url = summary.avatarURLString, + size = AvatarSize.RoomSelectRoomListItem, + ), + ) + Column( + modifier = Modifier + .padding(start = 12.dp, end = 4.dp, top = 4.dp, bottom = 4.dp) + .weight(1f) + ) { + // Name + Text( + style = ElementTheme.typography.fontBodyLgRegular, + text = summary.name, + color = ElementTheme.colors.textPrimary, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + // Alias + summary.canonicalAlias?.let { alias -> + Text( + text = alias, + color = ElementTheme.colors.textSecondary, + style = ElementTheme.typography.fontBodySmRegular, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + RadioButton(selected = isSelected, onClick = { onSelection(summary) }) + } +} + +@PreviewsDayNight +@Composable +internal fun RoomSelectViewPreview(@PreviewParameter(RoomSelectStateProvider::class) state: RoomSelectState) = ElementPreview { + RoomSelectView( + state = state, + onDismiss = {}, + onSubmit = {}, + ) +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt b/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTests.kt similarity index 53% rename from features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt rename to libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTests.kt index c3a70deac6..88ae560bb2 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt +++ b/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTests.kt @@ -14,35 +14,29 @@ * limitations under the License. */ -package io.element.android.features.messages.forward +package io.element.android.libraries.roomselect.impl import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import io.element.android.features.messages.impl.forward.ForwardMessagesEvents -import io.element.android.features.messages.impl.forward.ForwardMessagesPresenter +import io.element.android.libraries.roomselect.api.RoomSelectMode import io.element.android.libraries.designsystem.theme.components.SearchBarResultState -import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.roomlist.RoomSummary -import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.FakeMatrixClient -import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.aRoomSummaryDetail import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import io.element.android.tests.testutils.WarmUpRule import kotlinx.collections.immutable.persistentListOf -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test -class ForwardMessagesPresenterTests { +class RoomSelectPresenterTests { @get:Rule val warmUpRule = WarmUpRule() - @Test fun `present - initial state`() = runTest { val presenter = aPresenter() @@ -53,10 +47,6 @@ class ForwardMessagesPresenterTests { assertThat(initialState.selectedRooms).isEmpty() assertThat(initialState.resultState).isInstanceOf(SearchBarResultState.NotSearching::class.java) assertThat(initialState.isSearchActive).isFalse() - assertThat(initialState.isForwarding).isFalse() - assertThat(initialState.error).isNull() - assertThat(initialState.forwardingSucceeded).isNull() - // Search is run automatically val searchState = awaitItem() assertThat(searchState.resultState).isInstanceOf(SearchBarResultState.NoResults::class.java) @@ -72,10 +62,10 @@ class ForwardMessagesPresenterTests { val initialState = awaitItem() skipItems(1) - initialState.eventSink(ForwardMessagesEvents.ToggleSearchActive) + initialState.eventSink(RoomSelectEvents.ToggleSearchActive) assertThat(awaitItem().isSearchActive).isTrue() - initialState.eventSink(ForwardMessagesEvents.ToggleSearchActive) + initialState.eventSink(RoomSelectEvents.ToggleSearchActive) assertThat(awaitItem().isSearchActive).isFalse() } } @@ -93,67 +83,12 @@ class ForwardMessagesPresenterTests { val initialState = awaitItem() assertThat(awaitItem().resultState as? SearchBarResultState.Results).isEqualTo(SearchBarResultState.Results(listOf(aRoomSummaryDetail()))) - initialState.eventSink(ForwardMessagesEvents.UpdateQuery("string not contained")) + initialState.eventSink(RoomSelectEvents.UpdateQuery("string not contained")) assertThat(awaitItem().query).isEqualTo("string not contained") assertThat(awaitItem().resultState).isInstanceOf(SearchBarResultState.NoResults::class.java) } } - @Test - fun `present - select a room and forward successful`() = runTest { - val presenter = aPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() - skipItems(1) - val summary = aRoomSummaryDetail() - - initialState.eventSink(ForwardMessagesEvents.SetSelectedRoom(summary)) - awaitItem() - - // Test successful forwarding - initialState.eventSink(ForwardMessagesEvents.ForwardEvent) - - val forwardingState = awaitItem() - assertThat(forwardingState.isSearchActive).isFalse() - assertThat(forwardingState.isForwarding).isTrue() - - val successfulForwardState = awaitItem() - assertThat(successfulForwardState.isForwarding).isFalse() - assertThat(successfulForwardState.forwardingSucceeded).isNotNull() - } - } - - @Test - fun `present - select a room and forward failed, then clear`() = runTest { - val room = FakeMatrixRoom() - val presenter = aPresenter(fakeMatrixRoom = room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() - skipItems(1) - val summary = aRoomSummaryDetail() - - initialState.eventSink(ForwardMessagesEvents.SetSelectedRoom(summary)) - awaitItem() - - // Test failed forwarding - room.givenForwardEventResult(Result.failure(Throwable("error"))) - initialState.eventSink(ForwardMessagesEvents.ForwardEvent) - skipItems(1) - - val failedForwardState = awaitItem() - assertThat(failedForwardState.isForwarding).isFalse() - assertThat(failedForwardState.error).isNotNull() - - // Then clear error - initialState.eventSink(ForwardMessagesEvents.ClearError) - assertThat(awaitItem().error).isNull() - } - } - @Test fun `present - select and remove a room`() = runTest { val presenter = aPresenter() @@ -164,18 +99,19 @@ class ForwardMessagesPresenterTests { skipItems(1) val summary = aRoomSummaryDetail() - initialState.eventSink(ForwardMessagesEvents.SetSelectedRoom(summary)) + initialState.eventSink(RoomSelectEvents.SetSelectedRoom(summary)) assertThat(awaitItem().selectedRooms).isEqualTo(persistentListOf(summary)) - initialState.eventSink(ForwardMessagesEvents.RemoveSelectedRoom) + initialState.eventSink(RoomSelectEvents.RemoveSelectedRoom) assertThat(awaitItem().selectedRooms).isEmpty() } } - private fun CoroutineScope.aPresenter( - eventId: EventId = AN_EVENT_ID, - fakeMatrixRoom: FakeMatrixRoom = FakeMatrixRoom(), - coroutineScope: CoroutineScope = this, + private fun aPresenter( + mode: RoomSelectMode = RoomSelectMode.Forward, client: FakeMatrixClient = FakeMatrixClient(), - ) = ForwardMessagesPresenter(eventId.value, fakeMatrixRoom, coroutineScope, client) + ) = RoomSelectPresenter( + mode = mode, + client = client, + ) } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/ElementRichTextEditorStyle.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/ElementRichTextEditorStyle.kt index e5023d448a..a7620cced0 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/ElementRichTextEditorStyle.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/ElementRichTextEditorStyle.kt @@ -16,30 +16,52 @@ package io.element.android.libraries.textcomposer -import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.LocalTextStyle import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.isSpecified +import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.theme.bgSubtleTertiary -import io.element.android.libraries.theme.ElementTheme import io.element.android.wysiwyg.compose.RichTextEditorDefaults import io.element.android.wysiwyg.compose.RichTextEditorStyle -internal object ElementRichTextEditorStyle { +object ElementRichTextEditorStyle { @Composable - fun create( + fun composerStyle( hasFocus: Boolean, ) : RichTextEditorStyle { + val baseStyle = common() + return baseStyle.copy( + text = baseStyle.text.copy( + color = if (hasFocus) { + ElementTheme.materialColors.primary + } else { + ElementTheme.materialColors.secondary + }, + lineHeight = TextUnit.Unspecified, + includeFontPadding = true, + ) + ) + } + + @Composable + fun textStyle(): RichTextEditorStyle { + return common() + } + + @Composable + private fun common(): RichTextEditorStyle { val colors = ElementTheme.colors - val m3colors = MaterialTheme.colorScheme val codeCornerRadius = 4.dp val codeBorderWidth = 1.dp return RichTextEditorDefaults.style( text = RichTextEditorDefaults.textStyle( - color = if (hasFocus) { - m3colors.primary - } else { - m3colors.secondary - } + color = LocalTextStyle.current.color.takeIf { it.isSpecified } ?: LocalContentColor.current, + fontStyle = LocalTextStyle.current.fontStyle, + lineHeight = LocalTextStyle.current.lineHeight, + includeFontPadding = false, ), cursor = RichTextEditorDefaults.cursorStyle( color = colors.iconAccentTertiary, diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index 75dbc6c1c0..0d6d01f000 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -44,18 +44,19 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color +import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.media.createFakeWaveform import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UserId @@ -68,23 +69,21 @@ import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import io.element.android.libraries.textcomposer.components.ComposerOptionsButton import io.element.android.libraries.textcomposer.components.DismissTextFormattingButton -import io.element.android.libraries.textcomposer.components.VoiceMessageRecorderButton import io.element.android.libraries.textcomposer.components.SendButton import io.element.android.libraries.textcomposer.components.TextFormatting import io.element.android.libraries.textcomposer.components.VoiceMessageDeleteButton import io.element.android.libraries.textcomposer.components.VoiceMessagePreview +import io.element.android.libraries.textcomposer.components.VoiceMessageRecorderButton import io.element.android.libraries.textcomposer.components.VoiceMessageRecording import io.element.android.libraries.textcomposer.components.textInputRoundedCornerShape import io.element.android.libraries.textcomposer.mentions.rememberMentionSpanProvider import io.element.android.libraries.textcomposer.model.Message import io.element.android.libraries.textcomposer.model.MessageComposerMode -import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent import io.element.android.libraries.textcomposer.model.Suggestion import io.element.android.libraries.textcomposer.model.VoiceMessagePlayerEvent +import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent import io.element.android.libraries.textcomposer.model.VoiceMessageState -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings -import io.element.android.wysiwyg.compose.PillStyle import io.element.android.wysiwyg.compose.RichTextEditor import io.element.android.wysiwyg.compose.RichTextEditorState import io.element.android.wysiwyg.display.TextDisplay @@ -101,20 +100,20 @@ fun TextComposer( enableTextFormatting: Boolean, enableVoiceMessages: Boolean, currentUserId: UserId, + onRequestFocus: () -> Unit, + onSendMessage: (Message) -> Unit, + onResetComposerMode: () -> Unit, + onAddAttachment: () -> Unit, + onDismissTextFormatting: () -> Unit, + onVoiceRecorderEvent: (VoiceMessageRecorderEvent) -> Unit, + onVoicePlayerEvent: (VoiceMessagePlayerEvent) -> Unit, + onSendVoiceMessage: () -> Unit, + onDeleteVoiceMessage: () -> Unit, + onError: (Throwable) -> Unit, + onSuggestionReceived: (Suggestion?) -> Unit, modifier: Modifier = Modifier, showTextFormatting: Boolean = false, subcomposing: Boolean = false, - onRequestFocus: () -> Unit = {}, - onSendMessage: (Message) -> Unit = {}, - onResetComposerMode: () -> Unit = {}, - onAddAttachment: () -> Unit = {}, - onDismissTextFormatting: () -> Unit = {}, - onVoiceRecorderEvent: (VoiceMessageRecorderEvent) -> Unit = {}, - onVoicePlayerEvent: (VoiceMessagePlayerEvent) -> Unit = {}, - onSendVoiceMessage: () -> Unit = {}, - onDeleteVoiceMessage: () -> Unit = {}, - onError: (Throwable) -> Unit = {}, - onSuggestionReceived: (Suggestion?) -> Unit = {}, ) { val onSendClicked = { val html = if (enableTextFormatting) state.messageHtml else null @@ -441,11 +440,7 @@ private fun TextInput( modifier = Modifier .padding(top = 6.dp, bottom = 6.dp) .fillMaxWidth(), - style = ElementRichTextEditorStyle.create( - hasFocus = state.hasFocus - ).copy( - pill = PillStyle(Color.Red) - ), + style = ElementRichTextEditorStyle.composerStyle(hasFocus = state.hasFocus), resolveMentionDisplay = resolveMentionDisplay, resolveRoomMentionDisplay = resolveRoomMentionDisplay, onError = onError @@ -490,7 +485,7 @@ private fun EditingModeView( .padding(start = 12.dp) ) { Icon( - resourceId = CommonDrawables.ic_edit_solid, + imageVector = CompoundIcons.Edit, contentDescription = stringResource(CommonStrings.common_editing), tint = ElementTheme.materialColors.secondary, modifier = Modifier @@ -507,7 +502,7 @@ private fun EditingModeView( .weight(1f) ) Icon( - resourceId = CommonDrawables.ic_compound_close, + imageVector = CompoundIcons.Close, contentDescription = stringResource(CommonStrings.action_close), tint = ElementTheme.materialColors.secondary, modifier = Modifier @@ -554,7 +549,9 @@ private fun ReplyToModeView( ) { Text( text = senderName, - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .clipToBounds(), style = ElementTheme.typography.fontBodySmMedium, textAlign = TextAlign.Start, color = ElementTheme.materialColors.primary, @@ -570,7 +567,7 @@ private fun ReplyToModeView( ) } Icon( - resourceId = CommonDrawables.ic_compound_close, + imageVector = CompoundIcons.Close, contentDescription = stringResource(CommonStrings.action_close), tint = MaterialTheme.colorScheme.secondary, modifier = Modifier @@ -591,48 +588,40 @@ private fun ReplyToModeView( internal fun TextComposerSimplePreview() = ElementPreview { PreviewColumn(items = persistentListOf( { - TextComposer( + ATextComposer( RichTextEditorState("", initialFocus = true), voiceMessageState = VoiceMessageState.Idle, - onSendMessage = {}, composerMode = MessageComposerMode.Normal, - onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, currentUserId = UserId("@alice:localhost"), ) }, { - TextComposer( + ATextComposer( RichTextEditorState("A message", initialFocus = true), voiceMessageState = VoiceMessageState.Idle, - onSendMessage = {}, composerMode = MessageComposerMode.Normal, - onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, currentUserId = UserId("@alice:localhost") ) }, { - TextComposer( + ATextComposer( RichTextEditorState( "A message\nWith several lines\nTo preview larger textfields and long lines with overflow", initialFocus = true ), voiceMessageState = VoiceMessageState.Idle, - onSendMessage = {}, composerMode = MessageComposerMode.Normal, - onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, currentUserId = UserId("@alice:localhost") ) }, { - TextComposer( + ATextComposer( RichTextEditorState("A message without focus", initialFocus = false), voiceMessageState = VoiceMessageState.Idle, - onSendMessage = {}, composerMode = MessageComposerMode.Normal, - onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, currentUserId = UserId("@alice:localhost") @@ -645,7 +634,7 @@ internal fun TextComposerSimplePreview() = ElementPreview { @Composable internal fun TextComposerFormattingPreview() = ElementPreview { PreviewColumn(items = persistentListOf({ - TextComposer( + ATextComposer( RichTextEditorState("", initialFocus = false), voiceMessageState = VoiceMessageState.Idle, showTextFormatting = true, @@ -655,7 +644,7 @@ internal fun TextComposerFormattingPreview() = ElementPreview { currentUserId = UserId("@alice:localhost") ) }, { - TextComposer( + ATextComposer( RichTextEditorState("A message", initialFocus = false), voiceMessageState = VoiceMessageState.Idle, showTextFormatting = true, @@ -665,7 +654,7 @@ internal fun TextComposerFormattingPreview() = ElementPreview { currentUserId = UserId("@alice:localhost") ) }, { - TextComposer( + ATextComposer( RichTextEditorState("A message\nWith several lines\nTo preview larger textfields and long lines with overflow", initialFocus = false), voiceMessageState = VoiceMessageState.Idle, showTextFormatting = true, @@ -681,12 +670,10 @@ internal fun TextComposerFormattingPreview() = ElementPreview { @Composable internal fun TextComposerEditPreview() = ElementPreview { PreviewColumn(items = persistentListOf({ - TextComposer( + ATextComposer( RichTextEditorState("A message", initialFocus = true), voiceMessageState = VoiceMessageState.Idle, - onSendMessage = {}, composerMode = MessageComposerMode.Edit(EventId("$1234"), "Some text", TransactionId("1234")), - onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, currentUserId = UserId("@alice:localhost") @@ -698,10 +685,9 @@ internal fun TextComposerEditPreview() = ElementPreview { @Composable internal fun TextComposerReplyPreview() = ElementPreview { PreviewColumn(items = persistentListOf({ - TextComposer( + ATextComposer( RichTextEditorState(""), voiceMessageState = VoiceMessageState.Idle, - onSendMessage = {}, composerMode = MessageComposerMode.Reply( isThreaded = false, senderName = "Alice", @@ -711,17 +697,15 @@ internal fun TextComposerReplyPreview() = ElementPreview { "With several lines\n" + "To preview larger textfields and long lines with overflow" ), - onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, currentUserId = UserId("@alice:localhost") ) }, { - TextComposer( + ATextComposer( RichTextEditorState(""), voiceMessageState = VoiceMessageState.Idle, - onSendMessage = {}, composerMode = MessageComposerMode.Reply( isThreaded = true, senderName = "Alice", @@ -731,16 +715,14 @@ internal fun TextComposerReplyPreview() = ElementPreview { "With several lines\n" + "To preview larger textfields and long lines with overflow" ), - onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, currentUserId = UserId("@alice:localhost") ) }, { - TextComposer( + ATextComposer( RichTextEditorState("A message"), voiceMessageState = VoiceMessageState.Idle, - onSendMessage = {}, composerMode = MessageComposerMode.Reply( isThreaded = true, senderName = "Alice", @@ -753,16 +735,14 @@ internal fun TextComposerReplyPreview() = ElementPreview { ), defaultContent = "image.jpg" ), - onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, currentUserId = UserId("@alice:localhost") ) }, { - TextComposer( + ATextComposer( RichTextEditorState("A message"), voiceMessageState = VoiceMessageState.Idle, - onSendMessage = {}, composerMode = MessageComposerMode.Reply( isThreaded = false, senderName = "Alice", @@ -775,16 +755,14 @@ internal fun TextComposerReplyPreview() = ElementPreview { ), defaultContent = "video.mp4" ), - onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, currentUserId = UserId("@alice:localhost") ) }, { - TextComposer( + ATextComposer( RichTextEditorState("A message"), voiceMessageState = VoiceMessageState.Idle, - onSendMessage = {}, composerMode = MessageComposerMode.Reply( isThreaded = false, senderName = "Alice", @@ -797,16 +775,14 @@ internal fun TextComposerReplyPreview() = ElementPreview { ), defaultContent = "logs.txt" ), - onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, currentUserId = UserId("@alice:localhost") ) }, { - TextComposer( + ATextComposer( RichTextEditorState("A message", initialFocus = true), voiceMessageState = VoiceMessageState.Idle, - onSendMessage = {}, composerMode = MessageComposerMode.Reply( isThreaded = false, senderName = "Alice", @@ -819,7 +795,6 @@ internal fun TextComposerReplyPreview() = ElementPreview { ), defaultContent = "Shared location" ), - onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, currentUserId = UserId("@alice:localhost") @@ -834,12 +809,10 @@ internal fun TextComposerVoicePreview() = ElementPreview { @Composable fun VoicePreview( voiceMessageState: VoiceMessageState - ) = TextComposer( + ) = ATextComposer( RichTextEditorState("", initialFocus = true), voiceMessageState = voiceMessageState, - onSendMessage = {}, composerMode = MessageComposerMode.Normal, - onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, currentUserId = UserId("@alice:localhost") @@ -899,3 +872,35 @@ private fun PreviewColumn( } } } + +@Composable +private fun ATextComposer( + richTextEditorState: RichTextEditorState, + voiceMessageState: VoiceMessageState, + composerMode: MessageComposerMode, + enableTextFormatting: Boolean, + enableVoiceMessages: Boolean, + currentUserId: UserId, + showTextFormatting: Boolean = false, +) { + TextComposer( + state = richTextEditorState, + showTextFormatting = showTextFormatting, + voiceMessageState = voiceMessageState, + composerMode = composerMode, + enableTextFormatting = enableTextFormatting, + enableVoiceMessages = enableVoiceMessages, + currentUserId = currentUserId, + onRequestFocus = {}, + onSendMessage = {}, + onResetComposerMode = {}, + onAddAttachment = {}, + onDismissTextFormatting = {}, + onVoiceRecorderEvent = {}, + onVoicePlayerEvent = {}, + onSendVoiceMessage = {}, + onDeleteVoiceMessage = {}, + onError = {}, + onSuggestionReceived = {}, + ) +} diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposerLinkDialog.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposerLinkDialog.kt index 232e9ab0c1..82b84889d3 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposerLinkDialog.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposerLinkDialog.kt @@ -30,7 +30,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.ListItem import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.wysiwyg.view.models.LinkAction diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/ComposerOptionsButton.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/ComposerOptionsButton.kt index 8dbc33a0d8..c833b46b23 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/ComposerOptionsButton.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/ComposerOptionsButton.kt @@ -27,7 +27,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.textcomposer.R -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme @Composable internal fun ComposerOptionsButton( diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/DismissTextFormattingButton.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/DismissTextFormattingButton.kt index e4b4fa3216..e2cca41f1d 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/DismissTextFormattingButton.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/DismissTextFormattingButton.kt @@ -26,7 +26,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @Composable diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/FormattingOption.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/FormattingOption.kt index 791bf7cc75..850a0e3c38 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/FormattingOption.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/FormattingOption.kt @@ -32,33 +32,31 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.iconSuccessPrimaryBackground import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme -import io.element.android.libraries.theme.compound.generated.SemanticColors +import io.element.android.compound.theme.ElementTheme @Composable internal fun FormattingOption( state: FormattingOptionState, onClick: () -> Unit, imageVector: ImageVector, - contentDescription: String, + contentDescription: String?, modifier: Modifier = Modifier, - colors: SemanticColors = ElementTheme.colors, ) { val backgroundColor = when (state) { - FormattingOptionState.Selected -> colors.iconSuccessPrimaryBackground + FormattingOptionState.Selected -> ElementTheme.colors.iconSuccessPrimaryBackground FormattingOptionState.Default, FormattingOptionState.Disabled -> Color.Transparent } val foregroundColor = when (state) { - FormattingOptionState.Selected -> colors.iconSuccessPrimary - FormattingOptionState.Default -> colors.iconSecondary - FormattingOptionState.Disabled -> colors.iconDisabled + FormattingOptionState.Selected -> ElementTheme.colors.iconSuccessPrimary + FormattingOptionState.Default -> ElementTheme.colors.iconSecondary + FormattingOptionState.Disabled -> ElementTheme.colors.iconDisabled } Box( modifier = modifier @@ -98,19 +96,19 @@ internal fun FormattingButtonPreview() = ElementPreview { state = FormattingOptionState.Default, onClick = { }, imageVector = ImageVector.vectorResource(CommonDrawables.ic_bold), - contentDescription = "", + contentDescription = null, ) FormattingOption( state = FormattingOptionState.Selected, onClick = { }, imageVector = ImageVector.vectorResource(CommonDrawables.ic_italic), - contentDescription = "", + contentDescription = null, ) FormattingOption( state = FormattingOptionState.Disabled, onClick = { }, imageVector = ImageVector.vectorResource(CommonDrawables.ic_underline), - contentDescription = "", + contentDescription = null, ) } } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/LiveWaveformView.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/LiveWaveformView.kt index d92851c378..03bf4554f6 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/LiveWaveformView.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/LiveWaveformView.kt @@ -41,7 +41,7 @@ import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.components.media.drawWaveform import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toPersistentList import java.lang.Float.min diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt index eecd9d6c26..306fef8821 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt @@ -19,7 +19,6 @@ package io.element.android.libraries.textcomposer.components import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape @@ -30,13 +29,15 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import io.element.android.compound.icons.CompoundIcons +import io.element.android.compound.icons.compoundicons.Check +import io.element.android.compound.icons.compoundicons.Send +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton -import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.textcomposer.model.MessageComposerMode -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -52,14 +53,9 @@ internal fun SendButton( onClick = onClick, enabled = canSendMessage, ) { - val iconId = when (composerMode) { - is MessageComposerMode.Edit -> CommonDrawables.ic_compound_check - else -> CommonDrawables.ic_send - } - val iconSize = when (composerMode) { - is MessageComposerMode.Edit -> 24.dp - // CommonDrawables.ic_september_send is too big... reduce its size. - else -> 18.dp + val iconVector = when (composerMode) { + is MessageComposerMode.Edit -> CompoundIcons.Check + else -> CompoundIcons.Send } val iconStartPadding = when (composerMode) { is MessageComposerMode.Edit -> 0.dp @@ -77,10 +73,9 @@ internal fun SendButton( ) { Icon( modifier = Modifier - .height(iconSize) .padding(start = iconStartPadding) .align(Alignment.Center), - resourceId = iconId, + imageVector = iconVector, contentDescription = contentDescription, // Exception here, we use Color.White instead of ElementTheme.colors.iconOnSolidPrimary tint = if (canSendMessage) Color.White else ElementTheme.colors.iconDisabled diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageDeleteButton.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageDeleteButton.kt index 88f77f7075..3d4bf0ef3e 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageDeleteButton.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageDeleteButton.kt @@ -22,19 +22,19 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @Composable fun VoiceMessageDeleteButton( enabled: Boolean, + onClick: () -> Unit, modifier: Modifier = Modifier, - onClick: () -> Unit = {}, ) { IconButton( modifier = modifier @@ -44,7 +44,7 @@ fun VoiceMessageDeleteButton( ) { Icon( modifier = Modifier.size(24.dp), - resourceId = CommonDrawables.ic_compound_delete, + imageVector = CompoundIcons.Delete, contentDescription = stringResource(CommonStrings.a11y_delete), tint = if (enabled) { ElementTheme.colors.iconCriticalPrimary @@ -59,7 +59,13 @@ fun VoiceMessageDeleteButton( @Composable internal fun VoiceMessageDeleteButtonPreview() = ElementPreview { Row { - VoiceMessageDeleteButton(enabled = true) - VoiceMessageDeleteButton(enabled = false) + VoiceMessageDeleteButton( + enabled = true, + onClick = {}, + ) + VoiceMessageDeleteButton( + enabled = false, + onClick = {}, + ) } } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt index bcf307ec2e..08415e9e10 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt @@ -44,7 +44,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.utils.time.formatShort import kotlinx.collections.immutable.ImmutableList @@ -58,11 +58,11 @@ internal fun VoiceMessagePreview( showCursor: Boolean, waveform: ImmutableList, time: Duration, + onPlayClick: () -> Unit, + onPauseClick: () -> Unit, + onSeek: (Float) -> Unit, modifier: Modifier = Modifier, playbackProgress: Float = 0f, - onPlayClick: () -> Unit = {}, - onPauseClick: () -> Unit = {}, - onSeek: (Float) -> Unit = {}, ) { Row( modifier = modifier @@ -163,7 +163,7 @@ internal fun VoiceMessagePreviewPreview() = ElementPreview { Column( verticalArrangement = Arrangement.spacedBy(8.dp) ) { - VoiceMessagePreview( + AVoiceMessagePreview( isInteractive = true, isPlaying = true, time = 2.seconds, @@ -171,7 +171,7 @@ internal fun VoiceMessagePreviewPreview() = ElementPreview { showCursor = true, waveform = createFakeWaveform() ) - VoiceMessagePreview( + AVoiceMessagePreview( isInteractive = true, isPlaying = false, time = 0.seconds, @@ -179,7 +179,7 @@ internal fun VoiceMessagePreviewPreview() = ElementPreview { showCursor = true, waveform = createFakeWaveform() ) - VoiceMessagePreview( + AVoiceMessagePreview( isInteractive = false, isPlaying = false, time = 789.seconds, @@ -189,3 +189,25 @@ internal fun VoiceMessagePreviewPreview() = ElementPreview { ) } } + +@Composable +private fun AVoiceMessagePreview( + isInteractive: Boolean, + isPlaying: Boolean, + time: Duration, + playbackProgress: Float, + showCursor: Boolean, + waveform: ImmutableList, +) { + VoiceMessagePreview( + isInteractive = isInteractive, + isPlaying = isPlaying, + time = time, + playbackProgress = playbackProgress, + showCursor = showCursor, + waveform = waveform, + onPlayClick = {}, + onPauseClick = {}, + onSeek = {}, + ) +} diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecorderButton.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecorderButton.kt index d2a8c2cae1..01403ee3d7 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecorderButton.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecorderButton.kt @@ -34,14 +34,15 @@ import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.ui.strings.CommonStrings @Composable internal fun VoiceMessageRecorderButton( isRecording: Boolean, + onEvent: (VoiceMessageRecorderEvent) -> Unit, modifier: Modifier = Modifier, - onEvent: (VoiceMessageRecorderEvent) -> Unit = {}, ) { val hapticFeedback = LocalHapticFeedback.current @@ -78,7 +79,7 @@ private fun StartButton( ) { Icon( modifier = Modifier.size(24.dp), - resourceId = CommonDrawables.ic_compound_mic_on_outline, + imageVector = CompoundIcons.MicOnOutline, contentDescription = stringResource(CommonStrings.a11y_voice_message_record), tint = ElementTheme.colors.iconSecondary, ) @@ -113,7 +114,13 @@ private fun StopButton( @Composable internal fun VoiceMessageRecorderButtonPreview() = ElementPreview { Row { - VoiceMessageRecorderButton(isRecording = false) - VoiceMessageRecorderButton(isRecording = true) + VoiceMessageRecorderButton( + isRecording = false, + onEvent = {}, + ) + VoiceMessageRecorderButton( + isRecording = true, + onEvent = {}, + ) } } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecording.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecording.kt index 608a20200d..877e4832f4 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecording.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecording.kt @@ -41,7 +41,7 @@ import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.ui.utils.time.formatShort import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toPersistentList diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpan.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpan.kt index 609a050abf..ff93bb754a 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpan.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpan.kt @@ -19,24 +19,69 @@ package io.element.android.libraries.textcomposer.mentions import android.graphics.Canvas import android.graphics.Paint import android.graphics.RectF +import android.graphics.Typeface import android.text.style.ReplacementSpan import kotlin.math.roundToInt class MentionSpan( + val type: Type, val backgroundColor: Int, val textColor: Int, + val startPadding: Int, + val endPadding: Int, + val typeface: Typeface = Typeface.DEFAULT, ) : ReplacementSpan() { override fun getSize(paint: Paint, text: CharSequence?, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int { - return paint.measureText(text, start, end).roundToInt() + 40 + val mentionText = getActualText(text, start) + var actualEnd = end + if (mentionText != text.toString()) { + actualEnd = end + 1 + } + paint.typeface = typeface + return paint.measureText(mentionText, start, actualEnd).roundToInt() + startPadding + endPadding } override fun draw(canvas: Canvas, text: CharSequence?, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) { - val textSize = paint.measureText(text, start, end) - val rect = RectF(x, top.toFloat(), x + textSize + 40, bottom.toFloat()) + val mentionText = getActualText(text, start) + var actualEnd = end + if (mentionText != text.toString()) { + actualEnd = end + 1 + } + val textWidth = paint.measureText(mentionText, start, actualEnd) + // Extra vertical space to add below the baseline (y). This helps us center the span vertically + val extraVerticalSpace = y + paint.ascent() + paint.descent() - top + val rect = RectF(x, top.toFloat(), x + textWidth + startPadding + endPadding, y.toFloat() + extraVerticalSpace) paint.color = backgroundColor canvas.drawRoundRect(rect, rect.height() / 2, rect.height() / 2, paint) paint.color = textColor - canvas.drawText(text!!, start, end, x + 20, y.toFloat(), paint) + paint.typeface = typeface + canvas.drawText(mentionText, start, actualEnd, x + startPadding, y.toFloat(), paint) + } + + private fun getActualText(text: CharSequence?, start: Int): String { + return when (type) { + Type.USER -> { + val mentionText = text.toString() + if (start in mentionText.indices && mentionText[start] != '@') { + mentionText.replaceRange(start, start, "@") + } else { + mentionText + } + } + Type.ROOM -> { + val mentionText = text.toString() + if (start in mentionText.indices && mentionText[start] != '#') { + mentionText.replaceRange(start, start, "#") + } else { + mentionText + } + } + } + } + + enum class Type { + USER, + ROOM, } } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt index 77674e1254..f7b28d65e8 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt @@ -17,16 +17,24 @@ package io.element.android.libraries.textcomposer.mentions import android.graphics.Color +import android.graphics.Typeface import android.view.ViewGroup import android.widget.TextView +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import androidx.core.text.buildSpannedString +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.text.rememberTypeface import io.element.android.libraries.designsystem.theme.currentUserMentionPillBackground import io.element.android.libraries.designsystem.theme.currentUserMentionPillText import io.element.android.libraries.designsystem.theme.mentionPillBackground @@ -34,7 +42,6 @@ import io.element.android.libraries.designsystem.theme.mentionPillText import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkParser -import io.element.android.libraries.theme.ElementTheme @Stable class MentionSpanProvider( @@ -44,6 +51,10 @@ class MentionSpanProvider( private var otherTextColor: Int = 0, private var otherBackgroundColor: Int = Color.WHITE, ) { + private val paddingValues = PaddingValues(start = 4.dp, end = 6.dp) + + private val paddingValuesPx = mutableStateOf(0 to 0) + private val typeface = mutableStateOf(Typeface.DEFAULT) @Suppress("ComposableNaming") @Composable @@ -52,28 +63,48 @@ class MentionSpanProvider( currentUserBackgroundColor = ElementTheme.colors.currentUserMentionPillBackground.toArgb() otherTextColor = ElementTheme.colors.mentionPillText.toArgb() otherBackgroundColor = ElementTheme.colors.mentionPillBackground.toArgb() + + typeface.value = ElementTheme.typography.fontBodyLgMedium.rememberTypeface().value + with(LocalDensity.current) { + val leftPadding = paddingValues.calculateLeftPadding(LocalLayoutDirection.current).roundToPx() + val rightPadding = paddingValues.calculateRightPadding(LocalLayoutDirection.current).roundToPx() + paddingValuesPx.value = leftPadding to rightPadding + } } fun getMentionSpanFor(text: String, url: String): MentionSpan { val permalinkData = PermalinkParser.parse(url) + val (startPaddingPx, endPaddingPx) = paddingValuesPx.value return when { permalinkData is PermalinkData.UserLink -> { val isCurrentUser = permalinkData.userId == currentSessionId.value MentionSpan( + type = MentionSpan.Type.USER, backgroundColor = if (isCurrentUser) currentUserBackgroundColor else otherBackgroundColor, textColor = if (isCurrentUser) currentUserTextColor else otherTextColor, + startPadding = startPaddingPx, + endPadding = endPaddingPx, + typeface = typeface.value, ) } text == "@room" && permalinkData is PermalinkData.FallbackLink -> { MentionSpan( + type = MentionSpan.Type.USER, backgroundColor = otherBackgroundColor, textColor = otherTextColor, + startPadding = startPaddingPx, + endPadding = endPaddingPx, + typeface = typeface.value, ) } else -> { MentionSpan( + type = MentionSpan.Type.ROOM, backgroundColor = otherBackgroundColor, textColor = otherTextColor, + startPadding = startPaddingPx, + endPadding = endPaddingPx, + typeface = typeface.value, ) } } @@ -97,17 +128,26 @@ internal fun MentionSpanPreview() { provider.setup() val textColor = ElementTheme.colors.textPrimary.toArgb() - val mentionSpan = provider.getMentionSpanFor("me", "https://matrix.to/#/@me:matrix.org") - val mentionSpan2 = provider.getMentionSpanFor("other", "https://matrix.to/#/@other:matrix.org") + fun mentionSpanMe() = provider.getMentionSpanFor("me", "https://matrix.to/#/@me:matrix.org") + fun mentionSpanOther() = provider.getMentionSpanFor("other", "https://matrix.to/#/@other:matrix.org") + fun mentionSpanRoom() = provider.getMentionSpanFor("room", "https://matrix.to/#/#room:matrix.org") AndroidView(factory = { context -> TextView(context).apply { + includeFontPadding = false layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) text = buildSpannedString { append("This is a ") - append("@mention", mentionSpan, 0) + append("@mention", mentionSpanMe(), 0) append(" to the current user and this is a ") - append("@mention", mentionSpan2, 0) - append(" to other user") + append("@mention", mentionSpanOther(), 0) + append(" to other user. This one is for a room: ") + append("#room:matrix.org", mentionSpanRoom(), 0) + append("\n\n") + append("This ") + append("mention", mentionSpanMe(), 0) + append(" didn't have an '@' and it was automatically added, same as this ") + append("room:matrix.org", mentionSpanRoom(), 0) + append(" one, which had no leading '#'.") } setTextColor(textColor) } diff --git a/libraries/textcomposer/impl/src/main/res/values-de/translations.xml b/libraries/textcomposer/impl/src/main/res/values-de/translations.xml index 9a35f2b666..1bb9c78dbe 100644 --- a/libraries/textcomposer/impl/src/main/res/values-de/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-de/translations.xml @@ -21,4 +21,5 @@ "Ohne Einrückung" "Link" "Anhang hinzufügen" + "Zum Aufnehmen gedrückt halten"
diff --git a/libraries/textcomposer/impl/src/main/res/values-hu/translations.xml b/libraries/textcomposer/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..b4c96d4581 --- /dev/null +++ b/libraries/textcomposer/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,25 @@ + + + "Felsorolás be/ki" + "Formázási beállítások bezárása" + "Kódblokk be/ki" + "Üzenet…" + "Hivatkozás létrehozása" + "Hivatkozás szerkesztése" + "Félkövér formátum alkalmazása" + "Dőlt formátum alkalmazása" + "Áthúzott formátum alkalmazása" + "Aláhúzott formátum alkalmazása" + "Teljes képernyős mód be/ki" + "Behúzás" + "Soron belüli kód formátum alkalmazása" + "Hivatkozás beállítása" + "Számozott lista be/ki" + "Írási beállítások megnyitása" + "Idézet be/ki" + "Hivatkozás eltávolítása" + "Behúzás nélkül" + "Hivatkozás" + "Melléklet hozzáadása" + "Tartsa a rögzítéshez" + diff --git a/libraries/textcomposer/impl/src/main/res/values-in/translations.xml b/libraries/textcomposer/impl/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..8db2c18619 --- /dev/null +++ b/libraries/textcomposer/impl/src/main/res/values-in/translations.xml @@ -0,0 +1,25 @@ + + + "Alihkan daftar poin" + "Tutup opsi pemformatan" + "Alihkan blok kode" + "Kirim pesan…" + "Buat tautan" + "Sunting tautan" + "Terapkan format tebal" + "Terapkan format miring" + "Terapkan format coret" + "Terapkan format garis bawah" + "Alihkan mode layar penuh" + "Beri indentasi" + "Terapkan format kode dalam baris" + "Tetapkan tautan" + "Alihkan daftar bernomor" + "Buka opsi penulisan" + "Alihkan kutipan" + "Hapus tautan" + "Hapus indentasi" + "Tautan" + "Tambahkan lampiran" + "Tahan untuk merekam" + diff --git a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt index 748a06bb2c..b615dc489a 100644 --- a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt +++ b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt @@ -46,14 +46,21 @@ class MentionSpanProviderTest { @Test fun `getting mention span for current user should return a MentionSpan with custom colors`() { - val mentionSpan = mentionSpanProvider.getMentionSpanFor("me", "https://matrix.to/#/${currentUserId.value}") + val mentionSpan = mentionSpanProvider.getMentionSpanFor("@me:matrix.org", "https://matrix.to/#/${currentUserId.value}") assertThat(mentionSpan.backgroundColor).isEqualTo(myUserColor) assertThat(mentionSpan.textColor).isEqualTo(myUserColor) } @Test fun `getting mention span for other user should return a MentionSpan with normal colors`() { - val mentionSpan = mentionSpanProvider.getMentionSpanFor("other", "https://matrix.to/#/@other:matrix.org") + val mentionSpan = mentionSpanProvider.getMentionSpanFor("@other:matrix.org", "https://matrix.to/#/@other:matrix.org") + assertThat(mentionSpan.backgroundColor).isEqualTo(otherColor) + assertThat(mentionSpan.textColor).isEqualTo(otherColor) + } + + @Test + fun `getting mention span for a room should return a MentionSpan with normal colors`() { + val mentionSpan = mentionSpanProvider.getMentionSpanFor("#room:matrix.org", "https://matrix.to/#/#room:matrix.org") assertThat(mentionSpan.backgroundColor).isEqualTo(otherColor) assertThat(mentionSpan.textColor).isEqualTo(otherColor) } diff --git a/libraries/theme/README.md b/libraries/theme/README.md deleted file mode 100644 index 9d7bba33ee..0000000000 --- a/libraries/theme/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# Theme Module - -This module contains the theme tokens for the application, including those auto-generated from [Compound](https://github.com/vector-im/compound-design-tokens) and its mappings. - -## Usage - -The module contains public tokens and color schemes that are later used in `MaterialTheme` and added to `ElementTheme` for use in the application. - -All tokens can be accessed through the `ElementTheme` object, which contains the following properties: - -* `ElementTheme.materialColors`: contains all Material color tokens. In Figma, they're prefixed with `M3/`. It's an alias to `MaterialTheme.colorScheme`. -* `ElementTheme.colors`: contains all Compound semantic color tokens. In Figma, they're prefixed with either `Light/` or `Dark/`. -* `ElementTheme.typography`: contains the Compound `TypographyTokens` values. In Figma, they're prefixed with `Android/font/`. - -## Adding new tokens - -All new tokens **should** come from Compound and added to the `compound.generated` package. To map the literal tokens to the semantic ones, you'll have to update both `compoundColorsLight` and `compoundColorsDark` in `CompoundColors.kt`. diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ElementTheme.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ElementTheme.kt deleted file mode 100644 index 3b87565271..0000000000 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ElementTheme.kt +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.libraries.theme - -import android.os.Build -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.ColorScheme -import androidx.compose.material3.LocalContentColor -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Typography -import androidx.compose.material3.dynamicDarkColorScheme -import androidx.compose.material3.dynamicLightColorScheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.ReadOnlyComposable -import androidx.compose.runtime.SideEffect -import androidx.compose.runtime.remember -import androidx.compose.runtime.staticCompositionLocalOf -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext -import com.google.accompanist.systemuicontroller.SystemUiController -import com.google.accompanist.systemuicontroller.rememberSystemUiController -import io.element.android.libraries.theme.compound.compoundColorsDark -import io.element.android.libraries.theme.compound.compoundColorsLight -import io.element.android.libraries.theme.compound.compoundTypography -import io.element.android.libraries.theme.compound.generated.SemanticColors -import io.element.android.libraries.theme.compound.generated.TypographyTokens - -/** - * Inspired from https://medium.com/@lucasyujideveloper/54cbcbde1ace - */ -object ElementTheme { - /** - * The current [SemanticColors] provided by [ElementTheme]. - * These come from Compound and are the recommended colors to use for custom components. - * In Figma, these colors usually have the `Light/` or `Dark/` prefix. - */ - val colors: SemanticColors - @Composable - @ReadOnlyComposable - get() = LocalCompoundColors.current - - /** - * The current Material 3 [ColorScheme] provided by [ElementTheme], coming from [MaterialTheme]. - * In Figma, these colors usually have the `M3/` prefix. - */ - val materialColors: ColorScheme - @Composable - @ReadOnlyComposable - get() = MaterialTheme.colorScheme - - /** - * Compound [Typography] tokens. In Figma, these have the `Android/font/` prefix. - */ - val typography: TypographyTokens = TypographyTokens - - /** - * Material 3 [Typography] tokens. In Figma, these have the `M3 Typography/` prefix. - */ - val materialTypography: Typography - @Composable - @ReadOnlyComposable - get() = MaterialTheme.typography - - /** - * Returns whether the theme version used is the light or the dark one. - */ - val isLightTheme: Boolean - @Composable - @ReadOnlyComposable - get() = LocalCompoundColors.current.isLight -} - -/* Global variables (application level) */ -internal val LocalCompoundColors = staticCompositionLocalOf { compoundColorsLight } - -@Composable -fun ElementTheme( - darkTheme: Boolean = isSystemInDarkTheme(), - lightStatusBar: Boolean = !darkTheme, - dynamicColor: Boolean = false, /* true to enable MaterialYou */ - compoundColors: SemanticColors = if (darkTheme) compoundColorsDark else compoundColorsLight, - materialLightColors: ColorScheme = materialColorSchemeLight, - materialDarkColors: ColorScheme = materialColorSchemeDark, - typography: Typography = compoundTypography, - content: @Composable () -> Unit, -) { - val systemUiController = rememberSystemUiController() - val currentCompoundColor = remember(darkTheme) { - compoundColors.copy() - }.apply { updateColorsFrom(compoundColors) } - val colorScheme = when { - dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) - } - darkTheme -> materialDarkColors - else -> materialLightColors - } - val statusBarColorScheme = if (lightStatusBar) { - when { - dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { - val context = LocalContext.current - dynamicLightColorScheme(context) - } - else -> materialLightColors - } - } else { - colorScheme - } - SideEffect { - systemUiController.applyTheme(colorScheme = statusBarColorScheme, darkTheme = darkTheme && !lightStatusBar) - } - CompositionLocalProvider( - LocalCompoundColors provides currentCompoundColor, - LocalContentColor provides colorScheme.onSurface, - ) { - MaterialTheme( - colorScheme = colorScheme, - typography = typography, - content = content - ) - } -} - -internal fun SystemUiController.applyTheme( - colorScheme: ColorScheme, - darkTheme: Boolean, -) { - val useDarkIcons = !darkTheme - setStatusBarColor( - color = colorScheme.background - ) - setSystemBarsColor( - color = Color.Transparent, - darkIcons = useDarkIcons - ) -} diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ForcedDarkElementTheme.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ForcedDarkElementTheme.kt deleted file mode 100644 index c10ea74fc9..0000000000 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ForcedDarkElementTheme.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.libraries.theme - -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import com.google.accompanist.systemuicontroller.rememberSystemUiController - -/** - * Can be used to force a composable in dark theme. - * It will automatically change the system ui colors back to normal when leaving the composition. - */ -@Composable -fun ForcedDarkElementTheme( - lightStatusBar: Boolean = false, - content: @Composable () -> Unit, -) { - val systemUiController = rememberSystemUiController() - val colorScheme = MaterialTheme.colorScheme - val wasDarkTheme = !ElementTheme.colors.isLight - DisposableEffect(Unit) { - onDispose { - systemUiController.applyTheme(colorScheme, wasDarkTheme) - } - } - ElementTheme(darkTheme = true, lightStatusBar = lightStatusBar, content = content) -} diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/LegacyColors.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/LegacyColors.kt deleted file mode 100644 index 2e705c8c79..0000000000 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/LegacyColors.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.libraries.theme - -import androidx.compose.ui.graphics.Color -import io.element.android.libraries.theme.compound.generated.internal.DarkDesignTokens -import io.element.android.libraries.theme.compound.generated.internal.LightDesignTokens - -// ================================================================================================= -// IMPORTANT! -// We should not be adding any new colors here. This file is only for legacy colors. -// In fact, we should try to remove any references to these colors as we -// iterate through the designs. All new colors should come from Compound's Design Tokens. -// ================================================================================================= - -val LinkColor = Color(0xFF0086E6) - -val SnackBarLabelColorLight = LightDesignTokens.colorGray700 -val SnackBarLabelColorDark = DarkDesignTokens.colorGray700 diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt deleted file mode 100644 index 9df1db3505..0000000000 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.libraries.theme - -import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.lightColorScheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.tooling.preview.Preview -import io.element.android.libraries.theme.compound.generated.internal.DarkDesignTokens -import io.element.android.libraries.theme.compound.generated.internal.LightDesignTokens -import io.element.android.libraries.theme.previews.ColorsSchemePreview - -internal val materialColorSchemeLight = lightColorScheme( - primary = LightDesignTokens.colorGray1400, - onPrimary = LightDesignTokens.colorThemeBg, - primaryContainer = LightDesignTokens.colorThemeBg, - onPrimaryContainer = LightDesignTokens.colorGray1400, - inversePrimary = LightDesignTokens.colorThemeBg, - secondary = LightDesignTokens.colorGray900, - onSecondary = LightDesignTokens.colorThemeBg, - secondaryContainer = LightDesignTokens.colorGray400, - onSecondaryContainer = LightDesignTokens.colorGray1400, - tertiary = LightDesignTokens.colorGray900, - onTertiary = LightDesignTokens.colorThemeBg, - tertiaryContainer = LightDesignTokens.colorGray1400, - onTertiaryContainer = LightDesignTokens.colorThemeBg, - background = LightDesignTokens.colorThemeBg, - onBackground = LightDesignTokens.colorGray1400, - surface = LightDesignTokens.colorThemeBg, - onSurface = LightDesignTokens.colorGray1400, - surfaceVariant = LightDesignTokens.colorGray300, - onSurfaceVariant = LightDesignTokens.colorGray900, - surfaceTint = LightDesignTokens.colorGray1000, - inverseSurface = LightDesignTokens.colorGray1300, - inverseOnSurface = LightDesignTokens.colorThemeBg, - error = LightDesignTokens.colorRed900, - onError = LightDesignTokens.colorThemeBg, - errorContainer = LightDesignTokens.colorRed400, - onErrorContainer = LightDesignTokens.colorRed900, - outline = LightDesignTokens.colorGray800, - outlineVariant = LightDesignTokens.colorAlphaGray400, - scrim = LightDesignTokens.colorGray1400, -) - -internal val materialColorSchemeDark = darkColorScheme( - primary = DarkDesignTokens.colorGray1400, - onPrimary = DarkDesignTokens.colorThemeBg, - primaryContainer = DarkDesignTokens.colorThemeBg, - onPrimaryContainer = DarkDesignTokens.colorGray1400, - inversePrimary = DarkDesignTokens.colorThemeBg, - secondary = DarkDesignTokens.colorGray900, - onSecondary = DarkDesignTokens.colorThemeBg, - secondaryContainer = DarkDesignTokens.colorGray400, - onSecondaryContainer = DarkDesignTokens.colorGray1400, - tertiary = DarkDesignTokens.colorGray900, - onTertiary = DarkDesignTokens.colorThemeBg, - tertiaryContainer = DarkDesignTokens.colorGray1400, - onTertiaryContainer = DarkDesignTokens.colorThemeBg, - background = DarkDesignTokens.colorThemeBg, - onBackground = DarkDesignTokens.colorGray1400, - surface = DarkDesignTokens.colorThemeBg, - onSurface = DarkDesignTokens.colorGray1400, - surfaceVariant = DarkDesignTokens.colorGray300, - onSurfaceVariant = DarkDesignTokens.colorGray900, - surfaceTint = DarkDesignTokens.colorGray1000, - inverseSurface = DarkDesignTokens.colorGray1300, - inverseOnSurface = DarkDesignTokens.colorThemeBg, - error = DarkDesignTokens.colorRed900, - onError = DarkDesignTokens.colorThemeBg, - errorContainer = DarkDesignTokens.colorRed400, - onErrorContainer = DarkDesignTokens.colorRed900, - outline = DarkDesignTokens.colorGray800, - outlineVariant = DarkDesignTokens.colorAlphaGray400, - scrim = DarkDesignTokens.colorGray300, -) - -@Preview -@Composable -internal fun ColorsSchemeLightPreview() = ColorsSchemePreview( - Color.Black, - Color.White, - materialColorSchemeLight, -) - -@Preview -@Composable -internal fun ColorsSchemeDarkPreview() = ColorsSchemePreview( - Color.White, - Color.Black, - materialColorSchemeDark, -) diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/colors/AvatarColorsDark.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/colors/AvatarColorsDark.kt deleted file mode 100644 index a18721a60b..0000000000 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/colors/AvatarColorsDark.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.libraries.theme.colors - -import io.element.android.libraries.theme.compound.generated.internal.DarkDesignTokens - -/** - * Avatar colors are not yet part of SemanticColors, so create list here. - * DarkDesignTokens is internal to the module. - */ - -val avatarColorsDark = listOf( - DarkDesignTokens.colorBlue300 to DarkDesignTokens.colorBlue1200, - DarkDesignTokens.colorFuchsia300 to DarkDesignTokens.colorFuchsia1200, - DarkDesignTokens.colorGreen300 to DarkDesignTokens.colorGreen1200, - DarkDesignTokens.colorPink300 to DarkDesignTokens.colorPink1200, - DarkDesignTokens.colorOrange300 to DarkDesignTokens.colorOrange1200, - DarkDesignTokens.colorCyan300 to DarkDesignTokens.colorCyan1200, - DarkDesignTokens.colorPurple300 to DarkDesignTokens.colorPurple1200, - DarkDesignTokens.colorLime300 to DarkDesignTokens.colorLime1200, -) diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/colors/AvatarColorsLight.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/colors/AvatarColorsLight.kt deleted file mode 100644 index d2c320d479..0000000000 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/colors/AvatarColorsLight.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.libraries.theme.colors - -import io.element.android.libraries.theme.compound.generated.internal.LightDesignTokens - -/** - * Avatar colors are not yet part of SemanticColors, so create list here. - * LightDesignTokens is internal to the module. - */ - -val avatarColorsLight = listOf( - LightDesignTokens.colorBlue300 to LightDesignTokens.colorBlue1200, - LightDesignTokens.colorFuchsia300 to LightDesignTokens.colorFuchsia1200, - LightDesignTokens.colorGreen300 to LightDesignTokens.colorGreen1200, - LightDesignTokens.colorPink300 to LightDesignTokens.colorPink1200, - LightDesignTokens.colorOrange300 to LightDesignTokens.colorOrange1200, - LightDesignTokens.colorCyan300 to LightDesignTokens.colorCyan1200, - LightDesignTokens.colorPurple300 to LightDesignTokens.colorPurple1200, - LightDesignTokens.colorLime300 to LightDesignTokens.colorLime1200, -) diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/CompoundColors.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/CompoundColors.kt deleted file mode 100644 index f6b31cd678..0000000000 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/CompoundColors.kt +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.libraries.theme.compound - -import io.element.android.libraries.theme.compound.generated.internal.DarkDesignTokens -import io.element.android.libraries.theme.compound.generated.internal.LightDesignTokens -import io.element.android.libraries.theme.compound.generated.SemanticColors - -internal val compoundColorsLight = SemanticColors( - textPrimary = LightDesignTokens.colorGray1400, - textSecondary = LightDesignTokens.colorGray900, - textPlaceholder = LightDesignTokens.colorGray800, - textDisabled = LightDesignTokens.colorGray800, - textActionPrimary = LightDesignTokens.colorGray1400, - textActionAccent = LightDesignTokens.colorGreen900, - textLinkExternal = LightDesignTokens.colorBlue900, - textCriticalPrimary = LightDesignTokens.colorRed900, - textSuccessPrimary = LightDesignTokens.colorGreen900, - textInfoPrimary = LightDesignTokens.colorBlue900, - textOnSolidPrimary = LightDesignTokens.colorThemeBg, - bgSubtlePrimary = LightDesignTokens.colorGray400, - bgSubtleSecondary = LightDesignTokens.colorBgSubtleSecondaryLevel0, - bgCanvasDefault = LightDesignTokens.colorThemeBg, - bgCanvasDisabled = LightDesignTokens.colorGray200, - bgActionPrimaryRest = LightDesignTokens.colorGray1400, - bgActionPrimaryHovered = LightDesignTokens.colorGray1200, - bgActionPrimaryPressed = LightDesignTokens.colorGray1100, - bgActionPrimaryDisabled = LightDesignTokens.colorGray700, - bgActionSecondaryRest = LightDesignTokens.colorThemeBg, - bgActionSecondaryHovered = LightDesignTokens.colorAlphaGray200, - bgActionSecondaryPressed = LightDesignTokens.colorAlphaGray300, - bgCriticalPrimary = LightDesignTokens.colorRed900, - bgCriticalHovered = LightDesignTokens.colorRed1000, - bgCriticalSubtle = LightDesignTokens.colorRed200, - bgCriticalSubtleHovered = LightDesignTokens.colorRed300, - bgSuccessSubtle = LightDesignTokens.colorGreen200, - bgInfoSubtle = LightDesignTokens.colorBlue200, - borderDisabled = LightDesignTokens.colorGray500, - borderFocused = LightDesignTokens.colorBlue900, - borderInteractivePrimary = LightDesignTokens.colorGray800, - borderInteractiveSecondary = LightDesignTokens.colorGray600, - borderInteractiveHovered = LightDesignTokens.colorGray1100, - borderCriticalPrimary = LightDesignTokens.colorRed900, - borderCriticalHovered = LightDesignTokens.colorRed1000, - borderCriticalSubtle = LightDesignTokens.colorRed500, - borderSuccessSubtle = LightDesignTokens.colorGreen500, - borderInfoSubtle = LightDesignTokens.colorBlue500, - iconPrimary = LightDesignTokens.colorGray1400, - iconSecondary = LightDesignTokens.colorGray900, - iconTertiary = LightDesignTokens.colorGray800, - iconQuaternary = LightDesignTokens.colorGray700, - iconDisabled = LightDesignTokens.colorGray700, - iconPrimaryAlpha = LightDesignTokens.colorAlphaGray1400, - iconSecondaryAlpha = LightDesignTokens.colorAlphaGray900, - iconTertiaryAlpha = LightDesignTokens.colorAlphaGray800, - iconQuaternaryAlpha = LightDesignTokens.colorAlphaGray700, - iconAccentTertiary = LightDesignTokens.colorGreen800, - iconCriticalPrimary = LightDesignTokens.colorRed900, - iconSuccessPrimary = LightDesignTokens.colorGreen900, - iconInfoPrimary = LightDesignTokens.colorBlue900, - iconOnSolidPrimary = LightDesignTokens.colorThemeBg, - isLight = true, -) - -internal val compoundColorsDark = SemanticColors( - textPrimary = DarkDesignTokens.colorGray1400, - textSecondary = DarkDesignTokens.colorGray900, - textPlaceholder = DarkDesignTokens.colorGray800, - textDisabled = DarkDesignTokens.colorGray800, - textActionPrimary = DarkDesignTokens.colorGray1400, - textActionAccent = DarkDesignTokens.colorGreen900, - textLinkExternal = DarkDesignTokens.colorBlue900, - textCriticalPrimary = DarkDesignTokens.colorRed900, - textSuccessPrimary = DarkDesignTokens.colorGreen900, - textInfoPrimary = DarkDesignTokens.colorBlue900, - textOnSolidPrimary = DarkDesignTokens.colorThemeBg, - bgSubtlePrimary = DarkDesignTokens.colorGray400, - // The value DarkDesignTokens.colorBgSubtleSecondaryLevel0 is defined to colorThemeBg, this is not correct, so override the value here until this is fixed, - bgSubtleSecondary = DarkDesignTokens.colorGray300, // DarkDesignTokens.colorBgSubtleSecondaryLevel0 - bgCanvasDefault = DarkDesignTokens.colorThemeBg, - bgCanvasDisabled = DarkDesignTokens.colorGray200, - bgActionPrimaryRest = DarkDesignTokens.colorGray1400, - bgActionPrimaryHovered = DarkDesignTokens.colorGray1200, - bgActionPrimaryPressed = DarkDesignTokens.colorGray1100, - bgActionPrimaryDisabled = DarkDesignTokens.colorGray700, - bgActionSecondaryRest = DarkDesignTokens.colorThemeBg, - bgActionSecondaryHovered = DarkDesignTokens.colorAlphaGray200, - bgActionSecondaryPressed = DarkDesignTokens.colorAlphaGray300, - bgCriticalPrimary = DarkDesignTokens.colorRed900, - bgCriticalHovered = DarkDesignTokens.colorRed1000, - bgCriticalSubtle = DarkDesignTokens.colorRed200, - bgCriticalSubtleHovered = DarkDesignTokens.colorRed300, - bgSuccessSubtle = DarkDesignTokens.colorGreen200, - bgInfoSubtle = DarkDesignTokens.colorBlue200, - borderDisabled = DarkDesignTokens.colorGray500, - borderFocused = DarkDesignTokens.colorBlue900, - borderInteractivePrimary = DarkDesignTokens.colorGray800, - borderInteractiveSecondary = DarkDesignTokens.colorGray600, - borderInteractiveHovered = DarkDesignTokens.colorGray1100, - borderCriticalPrimary = DarkDesignTokens.colorRed900, - borderCriticalHovered = DarkDesignTokens.colorRed1000, - borderCriticalSubtle = DarkDesignTokens.colorRed500, - borderSuccessSubtle = DarkDesignTokens.colorGreen500, - borderInfoSubtle = DarkDesignTokens.colorBlue500, - iconPrimary = DarkDesignTokens.colorGray1400, - iconSecondary = DarkDesignTokens.colorGray900, - iconTertiary = DarkDesignTokens.colorGray800, - iconQuaternary = DarkDesignTokens.colorGray700, - iconDisabled = DarkDesignTokens.colorGray700, - iconPrimaryAlpha = DarkDesignTokens.colorAlphaGray1400, - iconSecondaryAlpha = DarkDesignTokens.colorAlphaGray900, - iconTertiaryAlpha = DarkDesignTokens.colorAlphaGray800, - iconQuaternaryAlpha = DarkDesignTokens.colorAlphaGray700, - iconAccentTertiary = DarkDesignTokens.colorGreen800, - iconCriticalPrimary = DarkDesignTokens.colorRed900, - iconSuccessPrimary = DarkDesignTokens.colorGreen900, - iconInfoPrimary = DarkDesignTokens.colorBlue900, - iconOnSolidPrimary = DarkDesignTokens.colorThemeBg, - isLight = false, -) diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/CompoundTypography.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/CompoundTypography.kt deleted file mode 100644 index 2da86f7882..0000000000 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/CompoundTypography.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.libraries.theme.compound - -import androidx.compose.material3.Typography -import androidx.compose.ui.text.PlatformTextStyle -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.LineHeightStyle -import androidx.compose.ui.unit.em -import androidx.compose.ui.unit.sp -import io.element.android.libraries.theme.compound.generated.TypographyTokens -import com.airbnb.android.showkase.annotation.ShowkaseTypography - -// 32px (Material) vs 34px, it's the closest one -@ShowkaseTypography(name = "M3 Headline Large", group = "Compound") -internal val compoundHeadingXlRegular = TypographyTokens.fontHeadingXlRegular - -// both are 28px -@ShowkaseTypography(name = "M3 Headline Medium", group = "Compound") -internal val compoundHeadingLgRegular = TypographyTokens.fontHeadingLgRegular - -// These are the default M3 values, but we're setting them manually so an update in M3 doesn't break our designs -@ShowkaseTypography(name = "M3 Headline Small", group = "Compound") -internal val defaultHeadlineSmall = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - lineHeight = 32.sp, - fontSize = 24.sp, - letterSpacing = 0.em, - platformStyle = PlatformTextStyle(includeFontPadding = false), - lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) -) - -// 22px (Material) vs 20px, it's the closest one -@ShowkaseTypography(name = "M3 Title Large", group = "Compound") -internal val compoundHeadingMdRegular = TypographyTokens.fontHeadingMdRegular - -// 16px both -@ShowkaseTypography(name = "M3 Title Medium", group = "Compound") -internal val compoundBodyLgMedium = TypographyTokens.fontBodyLgMedium - -// 14px both -@ShowkaseTypography(name = "M3 Title Small", group = "Compound") -internal val compoundBodyMdMedium = TypographyTokens.fontBodyMdMedium - -// 16px both -@ShowkaseTypography(name = "M3 Body Large", group = "Compound") -internal val compoundBodyLgRegular = TypographyTokens.fontBodyLgRegular - -// 14px both -@ShowkaseTypography(name = "M3 Body Medium", group = "Compound") -internal val compoundBodyMdRegular = TypographyTokens.fontBodyMdRegular - -// 12px both -@ShowkaseTypography(name = "M3 Body Small", group = "Compound") -internal val compoundBodySmRegular = TypographyTokens.fontBodySmRegular - -// 14px both, Title Small uses the same token so we have to declare it twice -@ShowkaseTypography(name = "M3 Label Large", group = "Compound") -internal val compoundBodyMdMedium_LabelLarge = TypographyTokens.fontBodyMdMedium - -// 12px both -@ShowkaseTypography(name = "M3 Label Medium", group = "Compound") -internal val compoundBodySmMedium = TypographyTokens.fontBodySmMedium - -// 11px both -@ShowkaseTypography(name = "M3 Label Small", group = "Compound") -internal val compoundBodyXsMedium = TypographyTokens.fontBodyXsMedium - -internal val compoundTypography = Typography( - // displayLarge = , 57px (Material) size. We have no equivalent - // displayMedium = , 45px (Material) size. We have no equivalent - // displaySmall = , 36px (Material) size. We have no equivalent - headlineLarge = compoundHeadingXlRegular, - headlineMedium = compoundHeadingLgRegular, - headlineSmall = defaultHeadlineSmall, - titleLarge = compoundHeadingMdRegular, - titleMedium = compoundBodyLgMedium, - titleSmall = compoundBodyMdMedium, - bodyLarge = compoundBodyLgRegular, - bodyMedium = compoundBodyMdRegular, - bodySmall = compoundBodySmRegular, - labelLarge = compoundBodyMdMedium_LabelLarge, - labelMedium = compoundBodySmMedium, - labelSmall = compoundBodyXsMedium, -) diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/generated/DO_NOT_MODIFY.txt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/generated/DO_NOT_MODIFY.txt deleted file mode 100644 index a6f7dd3f6a..0000000000 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/generated/DO_NOT_MODIFY.txt +++ /dev/null @@ -1 +0,0 @@ -Files inside this package are generated automatically from the Compound project (https://github.com/vector-im/compound-design-tokens) and will be batch-replaced when new tokens are generated. diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/generated/SemanticColors.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/generated/SemanticColors.kt deleted file mode 100644 index 2b3be49533..0000000000 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/generated/SemanticColors.kt +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright (c) 2023 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. - */ - -@file:Suppress("all") -package io.element.android.libraries.theme.compound.generated - -import androidx.compose.runtime.Stable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.compose.ui.graphics.Color - - - -// Do not edit directly -// Generated on Tue, 27 Jun 2023 11:49:05 GMT - - - - - -/** - * This class holds all the semantic tokens of the Compound theme. - */ -@Stable -class SemanticColors( - bgActionPrimaryDisabled: Color, - bgActionPrimaryHovered: Color, - bgActionPrimaryPressed: Color, - bgActionPrimaryRest: Color, - bgActionSecondaryHovered: Color, - bgActionSecondaryPressed: Color, - bgActionSecondaryRest: Color, - bgCanvasDefault: Color, - bgCanvasDisabled: Color, - bgCriticalHovered: Color, - bgCriticalPrimary: Color, - bgCriticalSubtle: Color, - bgCriticalSubtleHovered: Color, - bgInfoSubtle: Color, - bgSubtlePrimary: Color, - bgSubtleSecondary: Color, - bgSuccessSubtle: Color, - borderCriticalHovered: Color, - borderCriticalPrimary: Color, - borderCriticalSubtle: Color, - borderDisabled: Color, - borderFocused: Color, - borderInfoSubtle: Color, - borderInteractiveHovered: Color, - borderInteractivePrimary: Color, - borderInteractiveSecondary: Color, - borderSuccessSubtle: Color, - iconAccentTertiary: Color, - iconCriticalPrimary: Color, - iconDisabled: Color, - iconInfoPrimary: Color, - iconOnSolidPrimary: Color, - iconPrimary: Color, - iconPrimaryAlpha: Color, - iconQuaternary: Color, - iconQuaternaryAlpha: Color, - iconSecondary: Color, - iconSecondaryAlpha: Color, - iconSuccessPrimary: Color, - iconTertiary: Color, - iconTertiaryAlpha: Color, - textActionAccent: Color, - textActionPrimary: Color, - textCriticalPrimary: Color, - textDisabled: Color, - textInfoPrimary: Color, - textLinkExternal: Color, - textOnSolidPrimary: Color, - textPlaceholder: Color, - textPrimary: Color, - textSecondary: Color, - textSuccessPrimary: Color, - isLight: Boolean, -) { - var isLight by mutableStateOf(isLight) - private set - /** Background colour for primary actions. State: Disabled. */ - var bgActionPrimaryDisabled by mutableStateOf(bgActionPrimaryDisabled) - private set - /** Background colour for primary actions. State: Hover. */ - var bgActionPrimaryHovered by mutableStateOf(bgActionPrimaryHovered) - private set - /** Background colour for primary actions. State: Pressed. */ - var bgActionPrimaryPressed by mutableStateOf(bgActionPrimaryPressed) - private set - /** Background colour for primary actions. State: Rest. */ - var bgActionPrimaryRest by mutableStateOf(bgActionPrimaryRest) - private set - /** Background colour for secondary actions. State: Hover. */ - var bgActionSecondaryHovered by mutableStateOf(bgActionSecondaryHovered) - private set - /** Background colour for secondary actions. State: Pressed. */ - var bgActionSecondaryPressed by mutableStateOf(bgActionSecondaryPressed) - private set - /** Background colour for secondary actions. State: Rest. */ - var bgActionSecondaryRest by mutableStateOf(bgActionSecondaryRest) - private set - /** Default global background for the user interface. -Elevation: Default (Level 0) */ - var bgCanvasDefault by mutableStateOf(bgCanvasDefault) - private set - /** Default background for disabled elements. There's no minimum contrast requirement. */ - var bgCanvasDisabled by mutableStateOf(bgCanvasDisabled) - private set - /** High-contrast background color for critical state. State: Hover. */ - var bgCriticalHovered by mutableStateOf(bgCriticalHovered) - private set - /** High-contrast background color for critical state. State: Rest. */ - var bgCriticalPrimary by mutableStateOf(bgCriticalPrimary) - private set - /** Default subtle critical surfaces. State: Rest. */ - var bgCriticalSubtle by mutableStateOf(bgCriticalSubtle) - private set - /** Default subtle critical surfaces. State: Hover. */ - var bgCriticalSubtleHovered by mutableStateOf(bgCriticalSubtleHovered) - private set - /** Subtle background colour for informational elements. State: Rest. */ - var bgInfoSubtle by mutableStateOf(bgInfoSubtle) - private set - /** Medium contrast surfaces. -Elevation: Default (Level 2). */ - var bgSubtlePrimary by mutableStateOf(bgSubtlePrimary) - private set - /** Low contrast surfaces. -Elevation: Default (Level 1). */ - var bgSubtleSecondary by mutableStateOf(bgSubtleSecondary) - private set - /** Subtle background colour for success state elements. State: Rest. */ - var bgSuccessSubtle by mutableStateOf(bgSuccessSubtle) - private set - /** High-contrast border for critical state. State: Hover. */ - var borderCriticalHovered by mutableStateOf(borderCriticalHovered) - private set - /** High-contrast border for critical state. State: Rest. */ - var borderCriticalPrimary by mutableStateOf(borderCriticalPrimary) - private set - /** Subtle border colour for critical state elements. */ - var borderCriticalSubtle by mutableStateOf(borderCriticalSubtle) - private set - /** Used for borders of disabled elements. There's no minimum contrast requirement. */ - var borderDisabled by mutableStateOf(borderDisabled) - private set - /** Used for the focus state outline. */ - var borderFocused by mutableStateOf(borderFocused) - private set - /** Subtle border colour for informational elements. */ - var borderInfoSubtle by mutableStateOf(borderInfoSubtle) - private set - /** Default contrast for accessible interactive element borders. State: Hover. */ - var borderInteractiveHovered by mutableStateOf(borderInteractiveHovered) - private set - /** Default contrast for accessible interactive element borders. State: Rest. */ - var borderInteractivePrimary by mutableStateOf(borderInteractivePrimary) - private set - /** ⚠️ Lowest contrast for non-accessible interactive element borders, <3:1. Only use for non-essential borders. Do not rely exclusively on them. State: Rest. */ - var borderInteractiveSecondary by mutableStateOf(borderInteractiveSecondary) - private set - /** Subtle border colour for success state elements. */ - var borderSuccessSubtle by mutableStateOf(borderSuccessSubtle) - private set - /** Lowest contrast accessible accent icons. */ - var iconAccentTertiary by mutableStateOf(iconAccentTertiary) - private set - /** High-contrast icon for critical state. State: Rest. */ - var iconCriticalPrimary by mutableStateOf(iconCriticalPrimary) - private set - /** Use for icons in disabled elements. There's no minimum contrast requirement. */ - var iconDisabled by mutableStateOf(iconDisabled) - private set - /** High-contrast icon for informational elements. */ - var iconInfoPrimary by mutableStateOf(iconInfoPrimary) - private set - /** Highest contrast icon color on top of high-contrast solid backgrounds like primary, accent, or destructive actions. */ - var iconOnSolidPrimary by mutableStateOf(iconOnSolidPrimary) - private set - /** Highest contrast icons. */ - var iconPrimary by mutableStateOf(iconPrimary) - private set - /** Translucent version of primary icon. Refer to it for intended use. */ - var iconPrimaryAlpha by mutableStateOf(iconPrimaryAlpha) - private set - /** ⚠️ Lowest contrast non-accessible icons, <3:1. Only use for non-essential icons. Do not rely exclusively on them. */ - var iconQuaternary by mutableStateOf(iconQuaternary) - private set - /** Translucent version of quaternary icon. Refer to it for intended use. */ - var iconQuaternaryAlpha by mutableStateOf(iconQuaternaryAlpha) - private set - /** Lower contrast icons. */ - var iconSecondary by mutableStateOf(iconSecondary) - private set - /** Translucent version of secondary icon. Refer to it for intended use. */ - var iconSecondaryAlpha by mutableStateOf(iconSecondaryAlpha) - private set - /** High-contrast icon for success state elements. */ - var iconSuccessPrimary by mutableStateOf(iconSuccessPrimary) - private set - /** Lowest contrast accessible icons. */ - var iconTertiary by mutableStateOf(iconTertiary) - private set - /** Translucent version of tertiary icon. Refer to it for intended use. */ - var iconTertiaryAlpha by mutableStateOf(iconTertiaryAlpha) - private set - /** Accent text colour for plain actions. */ - var textActionAccent by mutableStateOf(textActionAccent) - private set - /** Default text colour for plain actions. */ - var textActionPrimary by mutableStateOf(textActionPrimary) - private set - /** Text colour for destructive plain actions. */ - var textCriticalPrimary by mutableStateOf(textCriticalPrimary) - private set - /** Use for regular text in disabled elements. There's no minimum contrast requirement. */ - var textDisabled by mutableStateOf(textDisabled) - private set - /** Accent text colour for informational elements. */ - var textInfoPrimary by mutableStateOf(textInfoPrimary) - private set - /** Text colour for external links. */ - var textLinkExternal by mutableStateOf(textLinkExternal) - private set - /** For use as text color on top of high-contrast solid backgrounds like primary, accent, or destructive actions. */ - var textOnSolidPrimary by mutableStateOf(textOnSolidPrimary) - private set - /** Use for placeholder text. Placeholder text should be non-essential. Do not rely exclusively on it. */ - var textPlaceholder by mutableStateOf(textPlaceholder) - private set - /** Highest contrast text. */ - var textPrimary by mutableStateOf(textPrimary) - private set - /** Lowest contrast text. */ - var textSecondary by mutableStateOf(textSecondary) - private set - /** Accent text colour for success state elements. */ - var textSuccessPrimary by mutableStateOf(textSuccessPrimary) - private set - - fun copy( - bgActionPrimaryDisabled: Color = this.bgActionPrimaryDisabled, - bgActionPrimaryHovered: Color = this.bgActionPrimaryHovered, - bgActionPrimaryPressed: Color = this.bgActionPrimaryPressed, - bgActionPrimaryRest: Color = this.bgActionPrimaryRest, - bgActionSecondaryHovered: Color = this.bgActionSecondaryHovered, - bgActionSecondaryPressed: Color = this.bgActionSecondaryPressed, - bgActionSecondaryRest: Color = this.bgActionSecondaryRest, - bgCanvasDefault: Color = this.bgCanvasDefault, - bgCanvasDisabled: Color = this.bgCanvasDisabled, - bgCriticalHovered: Color = this.bgCriticalHovered, - bgCriticalPrimary: Color = this.bgCriticalPrimary, - bgCriticalSubtle: Color = this.bgCriticalSubtle, - bgCriticalSubtleHovered: Color = this.bgCriticalSubtleHovered, - bgInfoSubtle: Color = this.bgInfoSubtle, - bgSubtlePrimary: Color = this.bgSubtlePrimary, - bgSubtleSecondary: Color = this.bgSubtleSecondary, - bgSuccessSubtle: Color = this.bgSuccessSubtle, - borderCriticalHovered: Color = this.borderCriticalHovered, - borderCriticalPrimary: Color = this.borderCriticalPrimary, - borderCriticalSubtle: Color = this.borderCriticalSubtle, - borderDisabled: Color = this.borderDisabled, - borderFocused: Color = this.borderFocused, - borderInfoSubtle: Color = this.borderInfoSubtle, - borderInteractiveHovered: Color = this.borderInteractiveHovered, - borderInteractivePrimary: Color = this.borderInteractivePrimary, - borderInteractiveSecondary: Color = this.borderInteractiveSecondary, - borderSuccessSubtle: Color = this.borderSuccessSubtle, - iconAccentTertiary: Color = this.iconAccentTertiary, - iconCriticalPrimary: Color = this.iconCriticalPrimary, - iconDisabled: Color = this.iconDisabled, - iconInfoPrimary: Color = this.iconInfoPrimary, - iconOnSolidPrimary: Color = this.iconOnSolidPrimary, - iconPrimary: Color = this.iconPrimary, - iconPrimaryAlpha: Color = this.iconPrimaryAlpha, - iconQuaternary: Color = this.iconQuaternary, - iconQuaternaryAlpha: Color = this.iconQuaternaryAlpha, - iconSecondary: Color = this.iconSecondary, - iconSecondaryAlpha: Color = this.iconSecondaryAlpha, - iconSuccessPrimary: Color = this.iconSuccessPrimary, - iconTertiary: Color = this.iconTertiary, - iconTertiaryAlpha: Color = this.iconTertiaryAlpha, - textActionAccent: Color = this.textActionAccent, - textActionPrimary: Color = this.textActionPrimary, - textCriticalPrimary: Color = this.textCriticalPrimary, - textDisabled: Color = this.textDisabled, - textInfoPrimary: Color = this.textInfoPrimary, - textLinkExternal: Color = this.textLinkExternal, - textOnSolidPrimary: Color = this.textOnSolidPrimary, - textPlaceholder: Color = this.textPlaceholder, - textPrimary: Color = this.textPrimary, - textSecondary: Color = this.textSecondary, - textSuccessPrimary: Color = this.textSuccessPrimary, - isLight: Boolean = this.isLight, - ) = SemanticColors( - bgActionPrimaryDisabled = bgActionPrimaryDisabled, - bgActionPrimaryHovered = bgActionPrimaryHovered, - bgActionPrimaryPressed = bgActionPrimaryPressed, - bgActionPrimaryRest = bgActionPrimaryRest, - bgActionSecondaryHovered = bgActionSecondaryHovered, - bgActionSecondaryPressed = bgActionSecondaryPressed, - bgActionSecondaryRest = bgActionSecondaryRest, - bgCanvasDefault = bgCanvasDefault, - bgCanvasDisabled = bgCanvasDisabled, - bgCriticalHovered = bgCriticalHovered, - bgCriticalPrimary = bgCriticalPrimary, - bgCriticalSubtle = bgCriticalSubtle, - bgCriticalSubtleHovered = bgCriticalSubtleHovered, - bgInfoSubtle = bgInfoSubtle, - bgSubtlePrimary = bgSubtlePrimary, - bgSubtleSecondary = bgSubtleSecondary, - bgSuccessSubtle = bgSuccessSubtle, - borderCriticalHovered = borderCriticalHovered, - borderCriticalPrimary = borderCriticalPrimary, - borderCriticalSubtle = borderCriticalSubtle, - borderDisabled = borderDisabled, - borderFocused = borderFocused, - borderInfoSubtle = borderInfoSubtle, - borderInteractiveHovered = borderInteractiveHovered, - borderInteractivePrimary = borderInteractivePrimary, - borderInteractiveSecondary = borderInteractiveSecondary, - borderSuccessSubtle = borderSuccessSubtle, - iconAccentTertiary = iconAccentTertiary, - iconCriticalPrimary = iconCriticalPrimary, - iconDisabled = iconDisabled, - iconInfoPrimary = iconInfoPrimary, - iconOnSolidPrimary = iconOnSolidPrimary, - iconPrimary = iconPrimary, - iconPrimaryAlpha = iconPrimaryAlpha, - iconQuaternary = iconQuaternary, - iconQuaternaryAlpha = iconQuaternaryAlpha, - iconSecondary = iconSecondary, - iconSecondaryAlpha = iconSecondaryAlpha, - iconSuccessPrimary = iconSuccessPrimary, - iconTertiary = iconTertiary, - iconTertiaryAlpha = iconTertiaryAlpha, - textActionAccent = textActionAccent, - textActionPrimary = textActionPrimary, - textCriticalPrimary = textCriticalPrimary, - textDisabled = textDisabled, - textInfoPrimary = textInfoPrimary, - textLinkExternal = textLinkExternal, - textOnSolidPrimary = textOnSolidPrimary, - textPlaceholder = textPlaceholder, - textPrimary = textPrimary, - textSecondary = textSecondary, - textSuccessPrimary = textSuccessPrimary, - isLight = isLight, - ) - - fun updateColorsFrom(other: SemanticColors) { - bgActionPrimaryDisabled = other.bgActionPrimaryDisabled - bgActionPrimaryHovered = other.bgActionPrimaryHovered - bgActionPrimaryPressed = other.bgActionPrimaryPressed - bgActionPrimaryRest = other.bgActionPrimaryRest - bgActionSecondaryHovered = other.bgActionSecondaryHovered - bgActionSecondaryPressed = other.bgActionSecondaryPressed - bgActionSecondaryRest = other.bgActionSecondaryRest - bgCanvasDefault = other.bgCanvasDefault - bgCanvasDisabled = other.bgCanvasDisabled - bgCriticalHovered = other.bgCriticalHovered - bgCriticalPrimary = other.bgCriticalPrimary - bgCriticalSubtle = other.bgCriticalSubtle - bgCriticalSubtleHovered = other.bgCriticalSubtleHovered - bgInfoSubtle = other.bgInfoSubtle - bgSubtlePrimary = other.bgSubtlePrimary - bgSubtleSecondary = other.bgSubtleSecondary - bgSuccessSubtle = other.bgSuccessSubtle - borderCriticalHovered = other.borderCriticalHovered - borderCriticalPrimary = other.borderCriticalPrimary - borderCriticalSubtle = other.borderCriticalSubtle - borderDisabled = other.borderDisabled - borderFocused = other.borderFocused - borderInfoSubtle = other.borderInfoSubtle - borderInteractiveHovered = other.borderInteractiveHovered - borderInteractivePrimary = other.borderInteractivePrimary - borderInteractiveSecondary = other.borderInteractiveSecondary - borderSuccessSubtle = other.borderSuccessSubtle - iconAccentTertiary = other.iconAccentTertiary - iconCriticalPrimary = other.iconCriticalPrimary - iconDisabled = other.iconDisabled - iconInfoPrimary = other.iconInfoPrimary - iconOnSolidPrimary = other.iconOnSolidPrimary - iconPrimary = other.iconPrimary - iconPrimaryAlpha = other.iconPrimaryAlpha - iconQuaternary = other.iconQuaternary - iconQuaternaryAlpha = other.iconQuaternaryAlpha - iconSecondary = other.iconSecondary - iconSecondaryAlpha = other.iconSecondaryAlpha - iconSuccessPrimary = other.iconSuccessPrimary - iconTertiary = other.iconTertiary - iconTertiaryAlpha = other.iconTertiaryAlpha - textActionAccent = other.textActionAccent - textActionPrimary = other.textActionPrimary - textCriticalPrimary = other.textCriticalPrimary - textDisabled = other.textDisabled - textInfoPrimary = other.textInfoPrimary - textLinkExternal = other.textLinkExternal - textOnSolidPrimary = other.textOnSolidPrimary - textPlaceholder = other.textPlaceholder - textPrimary = other.textPrimary - textSecondary = other.textSecondary - textSuccessPrimary = other.textSuccessPrimary - isLight = other.isLight - } -} diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/generated/TypographyTokens.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/generated/TypographyTokens.kt deleted file mode 100644 index 68ff1d2e03..0000000000 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/generated/TypographyTokens.kt +++ /dev/null @@ -1,164 +0,0 @@ - - -// Do not edit directly -// Generated on Fri, 28 Jul 2023 10:11:16 GMT - - - -@file:Suppress("all") -package io.element.android.libraries.theme.compound.generated - -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.unit.em -import androidx.compose.ui.unit.sp -import androidx.compose.ui.text.PlatformTextStyle -import androidx.compose.ui.text.style.LineHeightStyle - -object TypographyTokens { - val fontBodyLgMedium = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.W500, - lineHeight = 22.sp, - fontSize = 16.sp, - letterSpacing = 0.015629999999999998.em, - platformStyle = PlatformTextStyle(includeFontPadding = false), - lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) - ) - val fontBodyLgRegular = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.W400, - lineHeight = 22.sp, - fontSize = 16.sp, - letterSpacing = 0.015629999999999998.em, - platformStyle = PlatformTextStyle(includeFontPadding = false), - lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) - ) - val fontBodyMdMedium = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.W500, - lineHeight = 20.sp, - fontSize = 14.sp, - letterSpacing = 0.01786.em, - platformStyle = PlatformTextStyle(includeFontPadding = false), - lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) - ) - val fontBodyMdRegular = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.W400, - lineHeight = 20.sp, - fontSize = 14.sp, - letterSpacing = 0.01786.em, - platformStyle = PlatformTextStyle(includeFontPadding = false), - lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) - ) - val fontBodySmMedium = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.W500, - lineHeight = 17.sp, - fontSize = 12.sp, - letterSpacing = 0.03333.em, - platformStyle = PlatformTextStyle(includeFontPadding = false), - lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) - ) - val fontBodySmRegular = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.W400, - lineHeight = 17.sp, - fontSize = 12.sp, - letterSpacing = 0.03333.em, - platformStyle = PlatformTextStyle(includeFontPadding = false), - lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) - ) - val fontBodyXsMedium = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.W500, - lineHeight = 15.sp, - fontSize = 11.sp, - letterSpacing = 0.04545.em, - platformStyle = PlatformTextStyle(includeFontPadding = false), - lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) - ) - val fontBodyXsRegular = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.W400, - lineHeight = 15.sp, - fontSize = 11.sp, - letterSpacing = 0.04545.em, - platformStyle = PlatformTextStyle(includeFontPadding = false), - lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) - ) - val fontHeadingLgBold = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.W700, - lineHeight = 34.sp, - fontSize = 28.sp, - letterSpacing = 0.em, - platformStyle = PlatformTextStyle(includeFontPadding = false), - lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) - ) - val fontHeadingLgRegular = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.W400, - lineHeight = 34.sp, - fontSize = 28.sp, - letterSpacing = 0.em, - platformStyle = PlatformTextStyle(includeFontPadding = false), - lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) - ) - val fontHeadingMdBold = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.W700, - lineHeight = 27.sp, - fontSize = 22.sp, - letterSpacing = 0.em, - platformStyle = PlatformTextStyle(includeFontPadding = false), - lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) - ) - val fontHeadingMdRegular = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.W400, - lineHeight = 27.sp, - fontSize = 22.sp, - letterSpacing = 0.em, - platformStyle = PlatformTextStyle(includeFontPadding = false), - lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) - ) - val fontHeadingSmMedium = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.W500, - lineHeight = 25.sp, - fontSize = 20.sp, - letterSpacing = 0.em, - platformStyle = PlatformTextStyle(includeFontPadding = false), - lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) - ) - val fontHeadingSmRegular = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.W400, - lineHeight = 25.sp, - fontSize = 20.sp, - letterSpacing = 0.em, - platformStyle = PlatformTextStyle(includeFontPadding = false), - lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) - ) - val fontHeadingXlBold = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.W700, - lineHeight = 41.sp, - fontSize = 34.sp, - letterSpacing = 0.em, - platformStyle = PlatformTextStyle(includeFontPadding = false), - lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) - ) - val fontHeadingXlRegular = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.W400, - lineHeight = 41.sp, - fontSize = 34.sp, - letterSpacing = 0.em, - platformStyle = PlatformTextStyle(includeFontPadding = false), - lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) - ) -} diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/generated/internal/DarkDesignTokens.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/generated/internal/DarkDesignTokens.kt deleted file mode 100644 index 1930447899..0000000000 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/generated/internal/DarkDesignTokens.kt +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright (c) 2023 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. - */ - - -@file:Suppress("all") -package io.element.android.libraries.theme.compound.generated.internal - -import androidx.compose.ui.graphics.Color - -internal object DarkDesignTokens { - val colorAlphaPink1400 = Color(0xffffebef) - val colorAlphaPink1300 = Color(0xffffd1db) - val colorAlphaPink1200 = Color(0xffffadc0) - val colorAlphaPink1100 = Color(0xfffe86a4) - val colorAlphaPink1000 = Color(0xfaff6691) - val colorAlphaPink900 = Color(0xf5fe4382) - val colorAlphaPink800 = Color(0xccfe1b79) - val colorAlphaPink700 = Color(0x94fd1277) - val colorAlphaPink600 = Color(0x75fb0473) - val colorAlphaPink500 = Color(0xff6b0036) - val colorAlphaPink400 = Color(0xff570024) - val colorAlphaPink300 = Color(0xff470019) - val colorAlphaPink200 = Color(0xff3d0012) - val colorAlphaPink100 = Color(0xff38000f) - val colorAlphaFuchsia1400 = Color(0xfafdecfe) - val colorAlphaFuchsia1300 = Color(0xf2fde0ff) - val colorAlphaFuchsia1200 = Color(0xe8fac3fe) - val colorAlphaFuchsia1100 = Color(0xdbfaa4fe) - val colorAlphaFuchsia1000 = Color(0xd4f790fe) - val colorAlphaFuchsia900 = Color(0xccf172fd) - val colorAlphaFuchsia800 = Color(0xb5eb44fd) - val colorAlphaFuchsia700 = Color(0x8ad82ffe) - val colorAlphaFuchsia600 = Color(0x70d21fff) - val colorAlphaFuchsia500 = Color(0x61ca0aff) - val colorAlphaFuchsia400 = Color(0xff45005c) - val colorAlphaFuchsia300 = Color(0xff36004d) - val colorAlphaFuchsia200 = Color(0xff2d0042) - val colorAlphaFuchsia100 = Color(0xff28003d) - val colorAlphaPurple1400 = Color(0xffeeebff) - val colorAlphaPurple1300 = Color(0xffdfdbff) - val colorAlphaPurple1200 = Color(0xffc7bdff) - val colorAlphaPurple1100 = Color(0xffab9afe) - val colorAlphaPurple1000 = Color(0xfca28bfe) - val colorAlphaPurple900 = Color(0xfa9271fe) - val colorAlphaPurple800 = Color(0xeb7f4dff) - val colorAlphaPurple700 = Color(0xc2712bfd) - val colorAlphaPurple600 = Color(0xab690dfd) - val colorAlphaPurple500 = Color(0xff3d009e) - val colorAlphaPurple400 = Color(0xff2d0080) - val colorAlphaPurple300 = Color(0xff22006b) - val colorAlphaPurple200 = Color(0xff1d005c) - val colorAlphaPurple100 = Color(0xff1a0057) - val colorAlphaBlue1400 = Color(0xffe6effe) - val colorAlphaBlue1300 = Color(0xfccde1fe) - val colorAlphaBlue1200 = Color(0xf7a3c8ff) - val colorAlphaBlue1100 = Color(0xf57cb2fd) - val colorAlphaBlue1000 = Color(0xf062a0fe) - val colorAlphaBlue900 = Color(0xeb4491fd) - val colorAlphaBlue800 = Color(0xd61077fe) - val colorAlphaBlue700 = Color(0xa30665fe) - val colorAlphaBlue600 = Color(0x87015afe) - val colorAlphaBlue500 = Color(0xa1003cbd) - val colorAlphaBlue400 = Color(0xff001e70) - val colorAlphaBlue300 = Color(0xff001366) - val colorAlphaBlue200 = Color(0xff00095c) - val colorAlphaBlue100 = Color(0xff00055c) - val colorAlphaCyan1400 = Color(0xf5e1fbfe) - val colorAlphaCyan1300 = Color(0xebc9f7fd) - val colorAlphaCyan1200 = Color(0xd98af1ff) - val colorAlphaCyan1100 = Color(0xc926e7fd) - val colorAlphaCyan1000 = Color(0xe000bfe0) - val colorAlphaCyan900 = Color(0xff0091bd) - val colorAlphaCyan800 = Color(0xe0007ebd) - val colorAlphaCyan700 = Color(0xff00538a) - val colorAlphaCyan600 = Color(0xff003f75) - val colorAlphaCyan500 = Color(0xff003366) - val colorAlphaCyan400 = Color(0xff00265c) - val colorAlphaCyan300 = Color(0xff001b4d) - val colorAlphaCyan200 = Color(0xff001447) - val colorAlphaCyan100 = Color(0xff001142) - val colorAlphaGreen1400 = Color(0xf5e2fdf1) - val colorAlphaGreen1300 = Color(0xe8c4fde2) - val colorAlphaGreen1200 = Color(0xd486fdce) - val colorAlphaGreen1100 = Color(0xbd26fdbc) - val colorAlphaGreen1000 = Color(0xa61bfebd) - val colorAlphaGreen900 = Color(0x9412fdbe) - val colorAlphaGreen800 = Color(0xff007a62) - val colorAlphaGreen700 = Color(0xff005c45) - val colorAlphaGreen600 = Color(0xff004732) - val colorAlphaGreen500 = Color(0xff003d29) - val colorAlphaGreen400 = Color(0xff002e1b) - val colorAlphaGreen300 = Color(0xff002412) - val colorAlphaGreen200 = Color(0xff001f0e) - val colorAlphaGreen100 = Color(0xff001f0c) - val colorAlphaLime1400 = Color(0xf7e1fdd8) - val colorAlphaLime1300 = Color(0xebc3ffad) - val colorAlphaLime1200 = Color(0xd68dff5c) - val colorAlphaLime1100 = Color(0xbd71fd35) - val colorAlphaLime1000 = Color(0xa860fc2c) - val colorAlphaLime900 = Color(0x9454fd26) - val colorAlphaLime800 = Color(0x732dfd0d) - val colorAlphaLime700 = Color(0xff005c00) - val colorAlphaLime600 = Color(0xff004d00) - val colorAlphaLime500 = Color(0xff003d00) - val colorAlphaLime400 = Color(0xff002e00) - val colorAlphaLime300 = Color(0xff002900) - val colorAlphaLime200 = Color(0xff001f00) - val colorAlphaLime100 = Color(0xff001a00) - val colorAlphaYellow1400 = Color(0xffffedb3) - val colorAlphaYellow1300 = Color(0xfffeda58) - val colorAlphaYellow1200 = Color(0xf0fdc50d) - val colorAlphaYellow1100 = Color(0xffdba100) - val colorAlphaYellow1000 = Color(0xffcc8b00) - val colorAlphaYellow900 = Color(0xffbd7b00) - val colorAlphaYellow800 = Color(0xff9e5c00) - val colorAlphaYellow700 = Color(0xeb854200) - val colorAlphaYellow600 = Color(0xde753300) - val colorAlphaYellow500 = Color(0xff5c2300) - val colorAlphaYellow400 = Color(0xff4d1400) - val colorAlphaYellow300 = Color(0xff420900) - val colorAlphaYellow200 = Color(0xff380300) - val colorAlphaYellow100 = Color(0xff380000) - val colorAlphaOrange1400 = Color(0xffffeadb) - val colorAlphaOrange1300 = Color(0xffffd4b8) - val colorAlphaOrange1200 = Color(0xfcfdb781) - val colorAlphaOrange1100 = Color(0xf7fd953f) - val colorAlphaOrange1000 = Color(0xebfe8310) - val colorAlphaOrange900 = Color(0xd9fe740b) - val colorAlphaOrange800 = Color(0xb5ff5900) - val colorAlphaOrange700 = Color(0xbdc72800) - val colorAlphaOrange600 = Color(0xff850400) - val colorAlphaOrange500 = Color(0xff700000) - val colorAlphaOrange400 = Color(0xff570000) - val colorAlphaOrange300 = Color(0xff470000) - val colorAlphaOrange200 = Color(0xff3d0000) - val colorAlphaOrange100 = Color(0xff380000) - val colorAlphaRed1400 = Color(0xffffe8e5) - val colorAlphaRed1300 = Color(0xffffd3cc) - val colorAlphaRed1200 = Color(0xffffaea3) - val colorAlphaRed1100 = Color(0xffff857a) - val colorAlphaRed1000 = Color(0xffff645c) - val colorAlphaRed900 = Color(0xfffd3d3a) - val colorAlphaRed800 = Color(0xcffe2530) - val colorAlphaRed700 = Color(0x99fe0b24) - val colorAlphaRed600 = Color(0xff850009) - val colorAlphaRed500 = Color(0xff700000) - val colorAlphaRed400 = Color(0xff5c0000) - val colorAlphaRed300 = Color(0xff470000) - val colorAlphaRed200 = Color(0xff3d0000) - val colorAlphaRed100 = Color(0xff380000) - val colorAlphaGray1400 = Color(0xf2f6f9fe) - val colorAlphaGray1300 = Color(0xe3f2f7fd) - val colorAlphaGray1200 = Color(0xc9edf4fc) - val colorAlphaGray1100 = Color(0xade7f0fe) - val colorAlphaGray1000 = Color(0x9ce1eefe) - val colorAlphaGray900 = Color(0x8ae1effe) - val colorAlphaGray800 = Color(0x69e0edff) - val colorAlphaGray700 = Color(0x45e7f1fd) - val colorAlphaGray600 = Color(0x33eceff8) - val colorAlphaGray500 = Color(0x26f4f7fa) - val colorAlphaGray400 = Color(0x1aede7f4) - val colorAlphaGray300 = Color(0x0fe9dbf0) - val colorAlphaGray200 = Color(0x0ad9c3df) - val colorAlphaGray100 = Color(0x05d8dbdf) - val colorPink1400 = Color(0xffffe8ed) - val colorPink1300 = Color(0xffffd2dc) - val colorPink1200 = Color(0xffffabbe) - val colorPink1100 = Color(0xfffe84a2) - val colorPink1000 = Color(0xfffa658f) - val colorPink900 = Color(0xfff4427d) - val colorPink800 = Color(0xffce1865) - val colorPink700 = Color(0xff99114f) - val colorPink600 = Color(0xff7c0c41) - val colorPink500 = Color(0xff6d0036) - val colorPink400 = Color(0xff550024) - val colorPink300 = Color(0xff450018) - val colorPink200 = Color(0xff3c0012) - val colorPink100 = Color(0xff37000f) - val colorFuchsia1400 = Color(0xfff8e9f9) - val colorFuchsia1300 = Color(0xfff1d4f3) - val colorFuchsia1200 = Color(0xffe5b1e9) - val colorFuchsia1100 = Color(0xffd991de) - val colorFuchsia1000 = Color(0xffcf78d7) - val colorFuchsia900 = Color(0xffc560cf) - val colorFuchsia800 = Color(0xffaa36ba) - val colorFuchsia700 = Color(0xff7d2394) - val colorFuchsia600 = Color(0xff65177d) - val colorFuchsia500 = Color(0xff560f6f) - val colorFuchsia400 = Color(0xff46005e) - val colorFuchsia300 = Color(0xff37004e) - val colorFuchsia200 = Color(0xff2e0044) - val colorFuchsia100 = Color(0xff28003d) - val colorPurple1400 = Color(0xffeeebff) - val colorPurple1300 = Color(0xffdedaff) - val colorPurple1200 = Color(0xffc4baff) - val colorPurple1100 = Color(0xffad9cfe) - val colorPurple1000 = Color(0xff9e87fc) - val colorPurple900 = Color(0xff9171f9) - val colorPurple800 = Color(0xff7849ec) - val colorPurple700 = Color(0xff5a27c6) - val colorPurple600 = Color(0xff4a0db1) - val colorPurple500 = Color(0xff3d009e) - val colorPurple400 = Color(0xff2c0080) - val colorPurple300 = Color(0xff22006a) - val colorPurple200 = Color(0xff1c005a) - val colorPurple100 = Color(0xff1a0055) - val colorBlue1400 = Color(0xffe4eefe) - val colorBlue1300 = Color(0xffcbdffc) - val colorBlue1200 = Color(0xffa1c4f8) - val colorBlue1100 = Color(0xff7aacf4) - val colorBlue1000 = Color(0xff5e99f0) - val colorBlue900 = Color(0xff4187eb) - val colorBlue800 = Color(0xff0e67d9) - val colorBlue700 = Color(0xff0b49ab) - val colorBlue600 = Color(0xff083891) - val colorBlue500 = Color(0xff062d80) - val colorBlue400 = Color(0xff001e6f) - val colorBlue300 = Color(0xff001264) - val colorBlue200 = Color(0xff00095d) - val colorBlue100 = Color(0xff00055a) - val colorCyan1400 = Color(0xffdbf2f5) - val colorCyan1300 = Color(0xffb8e5eb) - val colorCyan1200 = Color(0xff78d0dc) - val colorCyan1100 = Color(0xff21bacd) - val colorCyan1000 = Color(0xff02a7c6) - val colorCyan900 = Color(0xff0093be) - val colorCyan800 = Color(0xff0271aa) - val colorCyan700 = Color(0xff005188) - val colorCyan600 = Color(0xff003f75) - val colorCyan500 = Color(0xff003468) - val colorCyan400 = Color(0xff002559) - val colorCyan300 = Color(0xff001b4e) - val colorCyan200 = Color(0xff001448) - val colorCyan100 = Color(0xff001144) - val colorGreen1400 = Color(0xffd9f4e7) - val colorGreen1300 = Color(0xffb5e8d1) - val colorGreen1200 = Color(0xff72d5ae) - val colorGreen1100 = Color(0xff1fc090) - val colorGreen1000 = Color(0xff17ac84) - val colorGreen900 = Color(0xff129a78) - val colorGreen800 = Color(0xff007a62) - val colorGreen700 = Color(0xff005a43) - val colorGreen600 = Color(0xff004832) - val colorGreen500 = Color(0xff003d29) - val colorGreen400 = Color(0xff002e1b) - val colorGreen300 = Color(0xff002513) - val colorGreen200 = Color(0xff001f0e) - val colorGreen100 = Color(0xff001c0b) - val colorLime1400 = Color(0xffdaf6d0) - val colorLime1300 = Color(0xffb6eca3) - val colorLime1200 = Color(0xff77d94f) - val colorLime1100 = Color(0xff56c02c) - val colorLime1000 = Color(0xff47ad26) - val colorLime900 = Color(0xff389b20) - val colorLime800 = Color(0xff1d7c13) - val colorLime700 = Color(0xff005c00) - val colorLime600 = Color(0xff004a00) - val colorLime500 = Color(0xff003e00) - val colorLime400 = Color(0xff003000) - val colorLime300 = Color(0xff002600) - val colorLime200 = Color(0xff002000) - val colorLime100 = Color(0xff001b00) - val colorYellow1400 = Color(0xffffedb1) - val colorYellow1300 = Color(0xfffedb58) - val colorYellow1200 = Color(0xffefbb0b) - val colorYellow1100 = Color(0xffdb9f00) - val colorYellow1000 = Color(0xffcc8c00) - val colorYellow900 = Color(0xffbc7a00) - val colorYellow800 = Color(0xff9d5b00) - val colorYellow700 = Color(0xff7c3e02) - val colorYellow600 = Color(0xff682e03) - val colorYellow500 = Color(0xff5c2400) - val colorYellow400 = Color(0xff4c1400) - val colorYellow300 = Color(0xff410900) - val colorYellow200 = Color(0xff3a0300) - val colorYellow100 = Color(0xff360000) - val colorOrange1400 = Color(0xffffeadb) - val colorOrange1300 = Color(0xffffd5b9) - val colorOrange1200 = Color(0xfffbb37e) - val colorOrange1100 = Color(0xfff6913d) - val colorOrange1000 = Color(0xffeb7a12) - val colorOrange900 = Color(0xffda670d) - val colorOrange800 = Color(0xffb94607) - val colorOrange700 = Color(0xff972206) - val colorOrange600 = Color(0xff830500) - val colorOrange500 = Color(0xff710000) - val colorOrange400 = Color(0xff580000) - val colorOrange300 = Color(0xff470000) - val colorOrange200 = Color(0xff3c0000) - val colorOrange100 = Color(0xff380000) - val colorRed1400 = Color(0xffffe9e6) - val colorRed1300 = Color(0xffffd4cd) - val colorRed1200 = Color(0xffffaea4) - val colorRed1100 = Color(0xffff877c) - val colorRed1000 = Color(0xffff665d) - val colorRed900 = Color(0xfffd3e3c) - val colorRed800 = Color(0xffd1212a) - val colorRed700 = Color(0xff9f0d1e) - val colorRed600 = Color(0xff830009) - val colorRed500 = Color(0xff710000) - val colorRed400 = Color(0xff590000) - val colorRed300 = Color(0xff470000) - val colorRed200 = Color(0xff3e0000) - val colorRed100 = Color(0xff370000) - val colorGray1400 = Color(0xffebeef2) - val colorGray1300 = Color(0xffd9dee4) - val colorGray1200 = Color(0xffbdc3cc) - val colorGray1100 = Color(0xffa3aab4) - val colorGray1000 = Color(0xff9199a4) - val colorGray900 = Color(0xff808994) - val colorGray800 = Color(0xff656c76) - val colorGray700 = Color(0xff4a4f55) - val colorGray600 = Color(0xff3c3f44) - val colorGray500 = Color(0xff323539) - val colorGray400 = Color(0xff26282d) - val colorGray300 = Color(0xff1d1f24) - val colorGray200 = Color(0xff181a1f) - val colorGray100 = Color(0xff14171b) - val colorThemeBg = Color(0xff101317) - val colorBgSubtleSecondaryLevel0 = colorThemeBg - val colorBgCanvasDefaultLevel1 = colorGray300 -} diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/generated/internal/LightDesignTokens.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/generated/internal/LightDesignTokens.kt deleted file mode 100644 index aed4a2e01f..0000000000 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/compound/generated/internal/LightDesignTokens.kt +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright (c) 2023 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. - */ - - -@file:Suppress("all") -package io.element.android.libraries.theme.compound.generated.internal; - -import androidx.compose.ui.graphics.Color - -internal object LightDesignTokens { - val colorAlphaPink1400 = Color(0xff420017) - val colorAlphaPink1300 = Color(0xff61002c) - val colorAlphaPink1200 = Color(0xfa79013d) - val colorAlphaPink1100 = Color(0xf79e004c) - val colorAlphaPink1000 = Color(0xf7b60256) - val colorAlphaPink900 = Color(0xf5cf025e) - val colorAlphaPink800 = Color(0xbff50052) - val colorAlphaPink700 = Color(0x78ff0040) - val colorAlphaPink600 = Color(0x54ff053f) - val colorAlphaPink500 = Color(0x3dff0037) - val colorAlphaPink400 = Color(0x21ff0037) - val colorAlphaPink300 = Color(0x14ff1447) - val colorAlphaPink200 = Color(0x0aff0537) - val colorAlphaPink100 = Color(0x05ff0537) - val colorAlphaFuchsia1400 = Color(0xff34004d) - val colorAlphaFuchsia1300 = Color(0xff4d0066) - val colorAlphaFuchsia1200 = Color(0xed5d0279) - val colorAlphaFuchsia1100 = Color(0xe073038c) - val colorAlphaFuchsia1000 = Color(0xd6820198) - val colorAlphaFuchsia900 = Color(0xcc9900ad) - val colorAlphaFuchsia800 = Color(0xa3ab03ba) - val colorAlphaFuchsia700 = Color(0x6eaa04b9) - val colorAlphaFuchsia600 = Color(0x4fb207bb) - val colorAlphaFuchsia500 = Color(0x3bb407c0) - val colorAlphaFuchsia400 = Color(0x21bd09c3) - val colorAlphaFuchsia300 = Color(0x12b60cc6) - val colorAlphaFuchsia200 = Color(0x0ab505cc) - val colorAlphaFuchsia100 = Color(0x05cc05cc) - val colorAlphaPurple1400 = Color(0xff200066) - val colorAlphaPurple1300 = Color(0xff34008f) - val colorAlphaPurple1200 = Color(0xfc4a02b6) - val colorAlphaPurple1100 = Color(0xdb4303c4) - val colorAlphaPurple1000 = Color(0xc94502d4) - val colorAlphaPurple900 = Color(0xba4902ed) - val colorAlphaPurple800 = Color(0x8f3b01f9) - val colorAlphaPurple700 = Color(0x613305ff) - val colorAlphaPurple600 = Color(0x452b05ff) - val colorAlphaPurple500 = Color(0x332605ff) - val colorAlphaPurple400 = Color(0x1f2f0fff) - val colorAlphaPurple300 = Color(0x12381aff) - val colorAlphaPurple200 = Color(0x0a5338ff) - val colorAlphaPurple100 = Color(0x053838ff) - val colorAlphaBlue1400 = Color(0xff000e66) - val colorAlphaBlue1300 = Color(0xff012579) - val colorAlphaBlue1200 = Color(0xfc013693) - val colorAlphaBlue1100 = Color(0xfa0148b2) - val colorAlphaBlue1000 = Color(0xfc0256c5) - val colorAlphaBlue900 = Color(0xfc0165df) - val colorAlphaBlue800 = Color(0xbf0062eb) - val colorAlphaBlue700 = Color(0x820264ed) - val colorAlphaBlue600 = Color(0x5e0663ef) - val colorAlphaBlue500 = Color(0x47096cf6) - val colorAlphaBlue400 = Color(0x290b6af9) - val colorAlphaBlue300 = Color(0x170a70ff) - val colorAlphaBlue200 = Color(0x0d2474ff) - val colorAlphaBlue100 = Color(0x08389cff) - val colorAlphaCyan1400 = Color(0xff001a52) - val colorAlphaCyan1300 = Color(0xff002c61) - val colorAlphaCyan1200 = Color(0xff003f75) - val colorAlphaCyan1100 = Color(0xff00568f) - val colorAlphaCyan1000 = Color(0xff00649e) - val colorAlphaCyan900 = Color(0xff0074ad) - val colorAlphaCyan800 = Color(0xff0095c2) - val colorAlphaCyan700 = Color(0xeb01b7cb) - val colorAlphaCyan600 = Color(0x8a01aac1) - val colorAlphaCyan500 = Color(0x6605abbd) - val colorAlphaCyan400 = Color(0x3800aabd) - val colorAlphaCyan300 = Color(0x1c00a8c2) - val colorAlphaCyan200 = Color(0x0f16abbb) - val colorAlphaCyan100 = Color(0x0816bbbb) - val colorAlphaGreen1400 = Color(0xff002411) - val colorAlphaGreen1300 = Color(0xff00331f) - val colorAlphaGreen1200 = Color(0xff004732) - val colorAlphaGreen1100 = Color(0xff005c45) - val colorAlphaGreen1000 = Color(0xff006b52) - val colorAlphaGreen900 = Color(0xff007a62) - val colorAlphaGreen800 = Color(0xff009975) - val colorAlphaGreen700 = Color(0xf501c18a) - val colorAlphaGreen600 = Color(0x8f01b76e) - val colorAlphaGreen500 = Color(0x6904b96a) - val colorAlphaGreen400 = Color(0x3b07b661) - val colorAlphaGreen300 = Color(0x1c00b85c) - val colorAlphaGreen200 = Color(0x0f16bb69) - val colorAlphaGreen100 = Color(0x0816bb79) - val colorAlphaLime1400 = Color(0xff002400) - val colorAlphaLime1300 = Color(0xff003800) - val colorAlphaLime1200 = Color(0xff004d00) - val colorAlphaLime1100 = Color(0xff006100) - val colorAlphaLime1000 = Color(0xff007000) - val colorAlphaLime900 = Color(0xf5107902) - val colorAlphaLime800 = Color(0xe8209301) - val colorAlphaLime700 = Color(0xdb39bd00) - val colorAlphaLime600 = Color(0xb540ce03) - val colorAlphaLime500 = Color(0x8237ca02) - val colorAlphaLime400 = Color(0x473ace09) - val colorAlphaLime300 = Color(0x262ecf02) - val colorAlphaLime200 = Color(0x1238d40c) - val colorAlphaLime100 = Color(0x0a4fcd1d) - val colorAlphaYellow1400 = Color(0xff420700) - val colorAlphaYellow1300 = Color(0xff571b00) - val colorAlphaYellow1200 = Color(0xff6b2e00) - val colorAlphaYellow1100 = Color(0xff804000) - val colorAlphaYellow1000 = Color(0xff8f4c00) - val colorAlphaYellow900 = Color(0xff9e5a00) - val colorAlphaYellow800 = Color(0xffbd7b00) - val colorAlphaYellow700 = Color(0xffe0a500) - val colorAlphaYellow600 = Color(0xfff0bc00) - val colorAlphaYellow500 = Color(0xfffacc00) - val colorAlphaYellow400 = Color(0x7dffc905) - val colorAlphaYellow300 = Color(0x40ffc905) - val colorAlphaYellow200 = Color(0x21ffc70f) - val colorAlphaYellow100 = Color(0x0fffcd05) - val colorAlphaOrange1400 = Color(0xff470000) - val colorAlphaOrange1300 = Color(0xff610000) - val colorAlphaOrange1200 = Color(0xff850000) - val colorAlphaOrange1100 = Color(0xff992100) - val colorAlphaOrange1000 = Color(0xffad3400) - val colorAlphaOrange900 = Color(0xffbd4500) - val colorAlphaOrange800 = Color(0xffdb6600) - val colorAlphaOrange700 = Color(0xbff56e00) - val colorAlphaOrange600 = Color(0x85fc6f03) - val colorAlphaOrange500 = Color(0x5eff6a00) - val colorAlphaOrange400 = Color(0x38ff6d05) - val colorAlphaOrange300 = Color(0x1cff6c0a) - val colorAlphaOrange200 = Color(0x12ff7d1a) - val colorAlphaOrange100 = Color(0x0aff8138) - val colorAlphaRed1400 = Color(0xff470000) - val colorAlphaRed1300 = Color(0xff610000) - val colorAlphaRed1200 = Color(0xff850007) - val colorAlphaRed1100 = Color(0xfca2011c) - val colorAlphaRed1000 = Color(0xf2bb0217) - val colorAlphaRed900 = Color(0xe8cf0213) - val colorAlphaRed800 = Color(0xc4ff0505) - val colorAlphaRed700 = Color(0x80ff1a05) - val colorAlphaRed600 = Color(0x5cff2205) - val colorAlphaRed500 = Color(0x45ff2605) - val colorAlphaRed400 = Color(0x26ff2b0a) - val colorAlphaRed300 = Color(0x14ff3814) - val colorAlphaRed200 = Color(0x0aff391f) - val colorAlphaRed100 = Color(0x08ff5938) - val colorAlphaGray1400 = Color(0xe6020408) - val colorAlphaGray1300 = Color(0xd603050c) - val colorAlphaGray1200 = Color(0xc402070d) - val colorAlphaGray1100 = Color(0xb5030b16) - val colorAlphaGray1000 = Color(0xa8030c1b) - val colorAlphaGray900 = Color(0x9c031021) - val colorAlphaGray800 = Color(0x8003152b) - val colorAlphaGray700 = Color(0x59011532) - val colorAlphaGray600 = Color(0x42011d3c) - val colorAlphaGray500 = Color(0x33052448) - val colorAlphaGray400 = Color(0x1f052e61) - val colorAlphaGray300 = Color(0x0f052657) - val colorAlphaGray200 = Color(0x0a366881) - val colorAlphaGray100 = Color(0x0536699b) - val colorPink1400 = Color(0xff430017) - val colorPink1300 = Color(0xff5f002b) - val colorPink1200 = Color(0xff7e0642) - val colorPink1100 = Color(0xff9f0850) - val colorPink1000 = Color(0xffb80a5b) - val colorPink900 = Color(0xffd20c65) - val colorPink800 = Color(0xfff7407d) - val colorPink700 = Color(0xffff88a6) - val colorPink600 = Color(0xffffadc0) - val colorPink500 = Color(0xffffc2cf) - val colorPink400 = Color(0xffffdee5) - val colorPink300 = Color(0xffffecf0) - val colorPink200 = Color(0xfffff5f7) - val colorPink100 = Color(0xfffffafb) - val colorFuchsia1400 = Color(0xff34004c) - val colorFuchsia1300 = Color(0xff4e0068) - val colorFuchsia1200 = Color(0xff671481) - val colorFuchsia1100 = Color(0xff822198) - val colorFuchsia1000 = Color(0xff972aaa) - val colorFuchsia900 = Color(0xffad33bd) - val colorFuchsia800 = Color(0xffc85ed1) - val colorFuchsia700 = Color(0xffdb93e1) - val colorFuchsia600 = Color(0xffe7b2ea) - val colorFuchsia500 = Color(0xffedc6f0) - val colorFuchsia400 = Color(0xfff6dff7) - val colorFuchsia300 = Color(0xfffaeefb) - val colorFuchsia200 = Color(0xfffcf5fd) - val colorFuchsia100 = Color(0xfffefafe) - val colorPurple1400 = Color(0xff200066) - val colorPurple1300 = Color(0xff33008d) - val colorPurple1200 = Color(0xff4c05b5) - val colorPurple1100 = Color(0xff5d26cd) - val colorPurple1000 = Color(0xff6b37de) - val colorPurple900 = Color(0xff7a47f1) - val colorPurple800 = Color(0xff9271fd) - val colorPurple700 = Color(0xffb1a0ff) - val colorPurple600 = Color(0xffc5bbff) - val colorPurple500 = Color(0xffd4cdff) - val colorPurple400 = Color(0xffe6e2ff) - val colorPurple300 = Color(0xfff1efff) - val colorPurple200 = Color(0xfff8f7ff) - val colorPurple100 = Color(0xfffbfbff) - val colorBlue1400 = Color(0xff000e65) - val colorBlue1300 = Color(0xff012478) - val colorBlue1200 = Color(0xff043894) - val colorBlue1100 = Color(0xff064ab1) - val colorBlue1000 = Color(0xff0558c7) - val colorBlue900 = Color(0xff0467dd) - val colorBlue800 = Color(0xff4088ee) - val colorBlue700 = Color(0xff7eaff6) - val colorBlue600 = Color(0xffa3c6fa) - val colorBlue500 = Color(0xffbad5fc) - val colorBlue400 = Color(0xffd8e7fe) - val colorBlue300 = Color(0xffe9f2ff) - val colorBlue200 = Color(0xfff4f8ff) - val colorBlue100 = Color(0xfff9fcff) - val colorCyan1400 = Color(0xff00194f) - val colorCyan1300 = Color(0xff002b61) - val colorCyan1200 = Color(0xff004077) - val colorCyan1100 = Color(0xff00548c) - val colorCyan1000 = Color(0xff00629c) - val colorCyan900 = Color(0xff0072ac) - val colorCyan800 = Color(0xff0094c0) - val colorCyan700 = Color(0xff15becf) - val colorCyan600 = Color(0xff76d1dd) - val colorCyan500 = Color(0xff9bdde5) - val colorCyan400 = Color(0xffc7ecf0) - val colorCyan300 = Color(0xffe3f5f8) - val colorCyan200 = Color(0xfff1fafb) - val colorCyan100 = Color(0xfff8fdfd) - val colorGreen1400 = Color(0xff002311) - val colorGreen1300 = Color(0xff003420) - val colorGreen1200 = Color(0xff004933) - val colorGreen1100 = Color(0xff005c45) - val colorGreen1000 = Color(0xff006b52) - val colorGreen900 = Color(0xff007a61) - val colorGreen800 = Color(0xff009b78) - val colorGreen700 = Color(0xff0bc491) - val colorGreen600 = Color(0xff71d7ae) - val colorGreen500 = Color(0xff98e1c1) - val colorGreen400 = Color(0xffc6eedb) - val colorGreen300 = Color(0xffe3f7ed) - val colorGreen200 = Color(0xfff1fbf6) - val colorGreen100 = Color(0xfff8fdfb) - val colorLime1400 = Color(0xff002400) - val colorLime1300 = Color(0xff003600) - val colorLime1200 = Color(0xff004b00) - val colorLime1100 = Color(0xff005f00) - val colorLime1000 = Color(0xff006e00) - val colorLime900 = Color(0xff197d0c) - val colorLime800 = Color(0xff359d18) - val colorLime700 = Color(0xff54c424) - val colorLime600 = Color(0xff76db4c) - val colorLime500 = Color(0xff99e57e) - val colorLime400 = Color(0xffc8f1ba) - val colorLime300 = Color(0xffe0f8d9) - val colorLime200 = Color(0xfff1fcee) - val colorLime100 = Color(0xfff8fdf6) - val colorYellow1400 = Color(0xff410600) - val colorYellow1300 = Color(0xff541a00) - val colorYellow1200 = Color(0xff692e00) - val colorYellow1100 = Color(0xff803f00) - val colorYellow1000 = Color(0xff8f4d00) - val colorYellow900 = Color(0xff9f5b00) - val colorYellow800 = Color(0xffbe7a00) - val colorYellow700 = Color(0xffdea200) - val colorYellow600 = Color(0xfff1bd00) - val colorYellow500 = Color(0xfffbce00) - val colorYellow400 = Color(0xffffe484) - val colorYellow300 = Color(0xfffff2c1) - val colorYellow200 = Color(0xfffff8e0) - val colorYellow100 = Color(0xfffffcf0) - val colorOrange1400 = Color(0xff450000) - val colorOrange1300 = Color(0xff620000) - val colorOrange1200 = Color(0xff850000) - val colorOrange1100 = Color(0xff9b2200) - val colorOrange1000 = Color(0xffac3300) - val colorOrange900 = Color(0xffbc4500) - val colorOrange800 = Color(0xffdc6700) - val colorOrange700 = Color(0xfff89440) - val colorOrange600 = Color(0xfffdb37c) - val colorOrange500 = Color(0xffffc8a1) - val colorOrange400 = Color(0xffffdfc8) - val colorOrange300 = Color(0xffffefe4) - val colorOrange200 = Color(0xfffff6ef) - val colorOrange100 = Color(0xfffffaf7) - val colorRed1400 = Color(0xff450000) - val colorRed1300 = Color(0xff620000) - val colorRed1200 = Color(0xff850006) - val colorRed1100 = Color(0xffa4041d) - val colorRed1000 = Color(0xffbc0f22) - val colorRed900 = Color(0xffd51928) - val colorRed800 = Color(0xffff3d3d) - val colorRed700 = Color(0xffff8c81) - val colorRed600 = Color(0xffffafa5) - val colorRed500 = Color(0xffffc5bc) - val colorRed400 = Color(0xffffdfda) - val colorRed300 = Color(0xffffefec) - val colorRed200 = Color(0xfffff7f6) - val colorRed100 = Color(0xfffffaf9) - val colorGray1400 = Color(0xff1b1d22) - val colorGray1300 = Color(0xff2b2d32) - val colorGray1200 = Color(0xff3c4045) - val colorGray1100 = Color(0xff4c5158) - val colorGray1000 = Color(0xff595e67) - val colorGray900 = Color(0xff656d77) - val colorGray800 = Color(0xff818a95) - val colorGray700 = Color(0xffa6adb7) - val colorGray600 = Color(0xffbdc4cc) - val colorGray500 = Color(0xffcdd3da) - val colorGray400 = Color(0xffe1e6ec) - val colorGray300 = Color(0xfff0f2f5) - val colorGray200 = Color(0xfff7f9fa) - val colorGray100 = Color(0xfffbfcfd) - val colorThemeBg = Color(0xffffffff) - val colorBgSubtleSecondaryLevel0 = colorGray300 - val colorBgCanvasDefaultLevel1 = colorThemeBg -} diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/previews/ColorListPreview.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/previews/ColorListPreview.kt deleted file mode 100644 index ad05cb3caa..0000000000 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/previews/ColorListPreview.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.libraries.theme.previews - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import kotlinx.collections.immutable.ImmutableMap - -@Composable -fun ColorListPreview( - backgroundColor: Color, - foregroundColor: Color, - colors: ImmutableMap, - modifier: Modifier = Modifier, -) { - Column( - modifier = modifier - .background(color = backgroundColor) - .fillMaxWidth() - ) { - colors.keys.forEach { name -> - val color = colors[name]!! - ColorPreview(backgroundColor = backgroundColor, foregroundColor = foregroundColor, name = name, color = color) - } - Spacer(modifier = Modifier.height(2.dp)) - } -} diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/previews/ColorPreview.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/previews/ColorPreview.kt deleted file mode 100644 index a139fee173..0000000000 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/previews/ColorPreview.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.libraries.theme.previews - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import io.element.android.libraries.theme.utils.toHrf - -@Composable -fun ColorPreview( - backgroundColor: Color, - foregroundColor: Color, - name: String, color: Color, - modifier: Modifier = Modifier, -) { - Column(modifier = modifier.fillMaxWidth()) { - Text(text = name + " " + color.toHrf(), fontSize = 6.sp, color = foregroundColor) - val backgroundBrush = Brush.linearGradient( - listOf( - backgroundColor, - foregroundColor, - ) - ) - Row( - modifier = Modifier.background(backgroundBrush) - ) { - repeat(2) { - Box( - modifier = Modifier - .padding(1.dp) - .background(color = color) - .height(10.dp) - .weight(1f) - ) - } - } - } -} diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/previews/ColorsSchemePreview.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/previews/ColorsSchemePreview.kt deleted file mode 100644 index db50eb0a3b..0000000000 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/previews/ColorsSchemePreview.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.libraries.theme.previews - -import androidx.compose.material3.ColorScheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import kotlinx.collections.immutable.persistentMapOf - -@Composable -internal fun ColorsSchemePreview( - backgroundColor: Color, - foregroundColor: Color, - colorScheme: ColorScheme, - modifier: Modifier = Modifier, -) { - val colors = persistentMapOf( - "primary" to colorScheme.primary, - "onPrimary" to colorScheme.onPrimary, - "primaryContainer" to colorScheme.primaryContainer, - "onPrimaryContainer" to colorScheme.onPrimaryContainer, - "inversePrimary" to colorScheme.inversePrimary, - "secondary" to colorScheme.secondary, - "onSecondary" to colorScheme.onSecondary, - "secondaryContainer" to colorScheme.secondaryContainer, - "onSecondaryContainer" to colorScheme.onSecondaryContainer, - "tertiary" to colorScheme.tertiary, - "onTertiary" to colorScheme.onTertiary, - "tertiaryContainer" to colorScheme.tertiaryContainer, - "onTertiaryContainer" to colorScheme.onTertiaryContainer, - "background" to colorScheme.background, - "onBackground" to colorScheme.onBackground, - "surface" to colorScheme.surface, - "onSurface" to colorScheme.onSurface, - "surfaceVariant" to colorScheme.surfaceVariant, - "onSurfaceVariant" to colorScheme.onSurfaceVariant, - "surfaceTint" to colorScheme.surfaceTint, - "inverseSurface" to colorScheme.inverseSurface, - "inverseOnSurface" to colorScheme.inverseOnSurface, - "error" to colorScheme.error, - "onError" to colorScheme.onError, - "errorContainer" to colorScheme.errorContainer, - "onErrorContainer" to colorScheme.onErrorContainer, - "outline" to colorScheme.outline, - "outlineVariant" to colorScheme.outlineVariant, - "scrim" to colorScheme.scrim, - ) - ColorListPreview( - backgroundColor = backgroundColor, - foregroundColor = foregroundColor, - colors = colors, - modifier = modifier, - ) -} diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/showkase/ThemeShowkaseRootModule.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/showkase/ThemeShowkaseRootModule.kt deleted file mode 100644 index ba6acad6b3..0000000000 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/showkase/ThemeShowkaseRootModule.kt +++ /dev/null @@ -1,23 +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 io.element.android.libraries.theme.showkase - -import com.airbnb.android.showkase.annotation.ShowkaseRoot -import com.airbnb.android.showkase.annotation.ShowkaseRootModule - -@ShowkaseRoot -class ThemeShowkaseRootModule : ShowkaseRootModule diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml index 9889bf4697..527ab2b866 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -2,13 +2,21 @@ "Smazat" "Skrýt heslo" + "Přejít dolů" "Pouze zmínky" "Ztišeno" + "Strana %1$d" "Pozastavit" "Pole pro PIN" "Přehrát" "Hlasování" "Hlasování ukončeno" + "Reagovat s %1$s" + "Reagovat s dalšími emoji" + "%1$s a %2$s přečetli" + "%1$s přečetl(a)" + "Klepnutím zobrazíte vše" + "Odstraňit reakci s %1$s" "Odeslat soubory" "Zobrazit heslo" "Zahájit hovor" @@ -31,9 +39,11 @@ "Vytvořit" "Vytvořit místnost" "Odmítnout" + "Odstranit hlasování" "Zakázat" "Hotovo" "Upravit" + "Upravit hlasování" "Povolit" "Ukončit hlasování" "Zadejte PIN" @@ -81,22 +91,26 @@ "Zahájit ověření" "Klepnutím načtete mapu" "Vyfotit" + "Klepnutím zobrazíte možnosti" "Zkusit znovu" "Zobrazit zdroj" "Ano" - "Upravit hlasování" + "Načíst více" "O aplikaci" "Zásady používání" "Pokročilá nastavení" "Analytika" + "Vzhled" "Zvuk" "Bubliny" "Záloha chatu" "Autorská práva" "Vytváření místnosti…" "Opustit místnost" + "Tmavé" "Chyba dešifrování" "Možnosti pro vývojáře" + "Přímý chat" "(upraveno)" "Úpravy" "* %1$s %2$s" @@ -113,11 +127,12 @@ "Nainstalovat APK" "Tento Matrix identifikátor nelze najít, takže pozvánka nemusí být přijata." "Opuštění místnosti" + "Světlý" "Odkaz zkopírován do schránky" "Načítání…" "Zpráva" "Akce zprávy" - "Rozložení zprávy" + "Zobrazení zpráv" "Zpráva byla odstraněna" "Moderní" "Ztlumit" @@ -146,7 +161,10 @@ "Hledat někoho" "Výsledky hledání" "Zabezpečení" + "Viděno" "Odesílání…" + "Odeslání se nezdařilo" + "Odesláno" "Server není podporován" "URL serveru" "Nastavení" @@ -157,6 +175,7 @@ "Úspěch" "Návrhy" "Synchronizace" + "Systém" "Text" "Oznámení třetích stran" "Vlákno" @@ -198,6 +217,11 @@ "zadány %1$d číslice" "zadáno %1$d číslic" + + "Přečetl(a) %1$s a %2$d další" + "Přečetl(a) %1$s a %2$d další" + "Přečetl(a) %1$s a %2$d dalších" + "%1$d člen" "%1$d členové" diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index e473e6efb9..085bb2c40a 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -1,13 +1,28 @@ + "Löschen" "Passwort verbergen" + "Nach unten springen" "Nur Erwähnungen" "Stummgeschaltet" + "Seite %1$d" + "Pausieren" + "PIN-Feld" + "Abspielen" "Umfrage" "Umfrage beendet" + "Reagiere mit %1$s" + "Mit anderen Emojis reagieren" + "Gelesen von %1$s und %2$s" + "Gelesen von %1$s" + "Tippe, um alle anzuzeigen" + "Reaktion mit %1$s entfernen" "Dateien senden" "Passwort anzeigen" + "Anruf starten" "Benutzermenü" + "Sprachnachricht aufnehmen." + "Aufnahme beenden" "Akzeptieren" "Zur Zeitleiste hinzufügen" "Zurück" @@ -24,11 +39,14 @@ "Erstellen" "Raum erstellen" "Ablehnen" + "Umfrage löschen" "Deaktivieren" "Erledigt" "Bearbeiten" + "Umfrage bearbeiten" "Aktivieren" "Umfrage beenden" + "PIN eingeben" "Passwort vergessen?" "Weiter" "Einladen" @@ -64,31 +82,41 @@ "Nachricht senden" "Teilen" "Link teilen" + "Erneut anmelden" + "Abmelden" + "Trotzdem abmelden" "Überspringen" "Start" "Chat starten" "Verifizierung starten" "Tippe, um die Karte zu laden" "Foto machen" + "Für Optionen tippen" + "Erneut versuchen" "Quelle anzeigen" "Ja" - "Umfrage bearbeiten" + "Mehr laden …" "Über" "Nutzungsrichtlinie" "Erweiterte Einstellungen" "Analysedaten" + "Erscheinungsbild" "Audio" - "Blasen" + "Sprechblasen" + "Chat-Backup" "Copyright" "Raum wird erstellt…" "Raum verlassen" + "Dunkel" "Dekodierungsfehler" "Entwickleroptionen" "(bearbeitet)" "Bearbeitung" "* %1$s %2$s" "Verschlüsselung aktiviert" + "PIN eingeben" "Fehler" + "Alle" "Datei" "Datei wurde unter Downloads gespeichert" "Nachricht weiterleiten" @@ -98,9 +126,11 @@ "APK installieren" "Diese Matrix-ID kann nicht gefunden werden, daher wird die Einladung möglicherweise nicht empfangen." "Raum verlassen" + "Hell" "Link in die Zwischenablage kopiert" "Laden…" "Nachricht" + "Nachrichtenaktionen" "Nachrichtenlayout" "Nachricht entfernt" "Modern" @@ -119,23 +149,31 @@ "Wird erneuert…" "%1$s antworten" "Einen Fehler melden" + "Ein Problem melden" "Bericht eingereicht" "Rich-Text-Editor" + "Raum" "Raumname" "z.B. dein Projektname" + "Bildschirmsperre" "Nach jemandem suchen" "Suchergebnisse" "Sicherheit" + "Gesehen von" "Wird gesendet…" + "Senden fehlgeschlagen" + "Gesendet" "Server wird nicht unterstützt" "Server-URL" "Einstellungen" "Geteilter Standort" + "Abmelden" "Chat wird gestartet…" "Sticker" "Erfolg" "Vorschläge" "Synchronisieren" + "System" "Text" "Hinweise von Drittanbietern" "Thread" @@ -144,28 +182,42 @@ "Entschlüsselung nicht möglich" "Einladungen konnten nicht an einen oder mehrere Benutzer gesendet werden." "Einladung(en) konnte(n) nicht gesendet werden" + "Entsperren" "Stummschaltung aufheben" "Nicht unterstütztes Ereignis" "Benutzername" "Verifizierung abgebrochen" "Verifizierung abgeschlossen" "Video" + "Sprachnachricht" "Warten…" + "Warte auf diese Nachricht" "Bist du sicher, dass du diese Umfrage beenden möchtest?" "Umfrage: %1$s" + "Gerät verifizieren" "Bestätigung" "Warnung" "Fehler beim Erstellen des Permalinks" "%1$s konnte die Karte nicht laden. Bitte versuche es später erneut." "Fehler beim Laden der Nachrichten" "%1$s konnte nicht auf deinen Standort zugreifen. Bitte versuche es später erneut." + "Fehler beim Hochladen der Sprachnachricht." "%1$s hat keine Erlaubnis, auf deinen Standort zuzugreifen. Du kannst den Zugriff in den Einstellungen aktivieren." "%1$s hat keine Erlaubnis, auf deinen Standort zuzugreifen. Aktiviere unten den Zugriff." + "%1$s hat nicht die Erlaubnis auf dein Mikrofon zuzugreifen. Aktiviere den Zugriff, um eine Sprachnachricht aufzunehmen." "Einige Nachrichten wurden nicht gesendet" "Entschuldigung, es ist ein Fehler aufgetreten" "🔐️ Begleite mich auf %1$s" "Hey, sprich mit mir auf %1$s: %2$s" "%1$s Android" + + "%1$d eingegebene Ziffer" + "%1$d eingegebene Ziffern" + + + "Gelesen von %1$s und %2$d anderen" + "Gelesen von %1$s und %2$d anderen" + "%1$d Mitglied" "%1$d Mitglieder" diff --git a/libraries/ui-strings/src/main/res/values-fr/translations.xml b/libraries/ui-strings/src/main/res/values-fr/translations.xml index ac67487989..60e4e78c2e 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -2,18 +2,27 @@ "Supprimer" "Masquer le mot de passe" + "Retourner à la fin de la conversation" "Mentions uniquement" "En sourdine" + "Page %1$d" "Pause" "Code PIN" "Lecture" "Sondage" "Sondage terminé" + "Réagir avec %1$s" + "Réagir avec d’autres emojis" + "Lu par %1$s et %2$s" + "Lu par %1$s" + "Taper pour voir toute la liste" + "Supprimer la réaction avec %1$s" "Envoyer des fichiers" "Afficher le mot de passe" "Démarrer un appel" "Menu utilisateur" - "Enregistrer un message vocal. Taper deux fois et maintenir pour enregistrer. Relâcher pour stopper l’enregistrement." + "Enregistrer un message vocal." + "Arrêter l\'enregistrement" "Accepter" "Ajouter à la discussion" "Retour" @@ -30,9 +39,11 @@ "Créer" "Créer un salon" "Refuser" + "Supprimer le sondage" "Désactiver" "Terminé" "Modifier" + "Modifier le sondage" "Activer" "Terminer le sondage" "Saisir le code PIN" @@ -80,22 +91,26 @@ "Commencer la vérification" "Cliquez pour charger la carte" "Prendre une photo" + "Appuyez pour afficher les options" "Essayer à nouveau" "Afficher la source" "Oui" - "Modifier le sondage" + "Voir plus" "À propos" "Politique d’utilisation acceptable" "Paramètres avancés" "Statistiques d’utilisation" + "Apparence" "Audio" "Bulles" "Sauvegarde des discussions" "Droits d’auteur" "Création du salon…" "Quitter le salon" + "Sombre" "Erreur de déchiffrement" "Options pour les développeurs" + "Discussion à deux" "(modifié)" "Édition" "* %1$s %2$s" @@ -112,9 +127,11 @@ "Installer l’APK" "Cet identifiant Matrix est introuvable, il est donc possible que l’invitation ne soit pas reçue." "Quitter le salon…" + "Clair" "Lien copié dans le presse-papiers" "Chargement…" "Message" + "Actions sur le message" "Mode d’affichage des messages" "Message supprimé" "Moderne" @@ -134,6 +151,7 @@ "Actualisation…" "En réponse à %1$s" "Signaler un problème" + "Remonter un problème" "Rapport soumis" "Éditeur de texte enrichi" "Salon" @@ -143,7 +161,10 @@ "Rechercher quelqu’un" "Résultats de la recherche" "Sécurité" + "Vu par" "Envoi en cours…" + "Échec de l\'envoi" + "Envoyé" "Serveur non pris en charge" "URL du serveur" "Paramètres" @@ -154,6 +175,7 @@ "Succès" "Suggestions" "Synchronisation" + "Système" "Texte" "Avis de tiers" "Fil de discussion" @@ -174,6 +196,7 @@ "En attente de la clé de déchiffrement" "Êtes-vous sûr de vouloir mettre fin à ce sondage ?" "Sondage : %1$s" + "Vérifier la session" "Confirmation" "Attention" "Échec de la création du permalien" @@ -193,6 +216,10 @@ "%1$d chiffre saisi" "%1$d chiffres saisis" + + "Lu par %1$s et %2$d autre" + "Lu par %1$s et %2$d autres" + "%1$d membre" "%1$d membres" diff --git a/libraries/ui-strings/src/main/res/values-hu/translations.xml b/libraries/ui-strings/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..f59c70a05a --- /dev/null +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -0,0 +1,246 @@ + + + "Törlés" + "Jelszó elrejtése" + "Ugrás az aljára" + "Csak megemlítések" + "Némítva" + "%1$d. oldal" + "Szüneteltetés" + "PIN-mező" + "Lejátszás" + "Szavazás" + "Befejezett szavazás" + "Reagálás a következővel: %1$s" + "Reagálás más emodzsikkal" + "Olvasta: %1$s és %2$s" + "Olvasta: %1$s" + "Koppintson az összes megjelenítéséhez" + "Reakció eltávolítása: %1$s" + "Fájlküldés" + "Jelszó megjelenítése" + "Hanghívás indítása" + "Felhasználói menü" + "Hangüzenet felvétele." + "Rögzítés leállítása" + "Elfogadás" + "Hozzáadás az idővonalhoz" + "Vissza" + "Mégse" + "Fénykép kiválasztása" + "Törlés" + "Bezárás" + "Ellenőrzés befejezése" + "Megerősítés" + "Folytatás" + "Másolás" + "Hivatkozás másolása" + "Üzenetre mutató hivatkozás másolása" + "Létrehozás" + "Szoba létrehozása" + "Elutasítás" + "Szavazás törlése" + "Letiltás" + "Kész" + "Szerkesztés" + "Szavazás szerkesztése" + "Engedélyezés" + "Szavazás lezárása" + "Adja meg a PIN-kódot" + "Elfelejtette a jelszót?" + "Tovább" + "Meghívás" + "Ismerősök meghívása" + "Ismerősök meghívása ide: %1$s" + "Emberek meghívása ide: %1$s" + "Meghívások" + "Csatlakozás" + "További tudnivalók" + "Elhagyás" + "Szoba elhagyása" + "Fiók kezelése" + "Eszközök kezelése" + "Következő" + "Nem" + "Most nem" + "Rendben" + "Beállítások megnyitása" + "Megnyitás a következővel" + "Gyors válasz" + "Idézet" + "Reakció" + "Eltávolítás" + "Válasz" + "Válasz az üzenetszálban" + "Hiba jelentése" + "Tartalom jelentése" + "Újra" + "Visszafejtés újbóli megpróbálása" + "Mentés" + "Keresés" + "Küldés" + "Üzenet küldése" + "Megosztás" + "Hivatkozás megosztása" + "Jelentkezzen be újra" + "Kijelentkezés" + "Kijelentkezés mindenképp" + "Kihagyás" + "Indítás" + "Csevegés indítása" + "Ellenőrzés elindítása" + "Koppintson a térkép betöltéséhez" + "Fénykép készítése" + "Koppintson a lehetőségekért" + "Próbálja újra" + "Forrás megtekintése" + "Igen" + "Továbbiak betöltése" + "Névjegy" + "Elfogadható használatra vonatkozó szabályzat" + "Speciális beállítások" + "Elemzések" + "Megjelenítés" + "Hang" + "Buborékok" + "Csevegés biztonsági mentése" + "Szerzői jogok" + "Szoba létrehozása…" + "Elhagyta a szobát" + "Sötét" + "Visszafejtési hiba" + "Fejlesztői beállítások" + "Közvetlen csevegés" + "(szerkesztve)" + "Szerkesztés" + "* %1$s %2$s" + "Titkosítás engedélyezve" + "Adja meg a PIN-kódját" + "Hiba" + "Mindenki" + "Fájl" + "A fájl a Letöltések mappába mentve" + "Üzenet továbbítása" + "GIF" + "Kép" + "Válasz erre: %1$s" + "APK telepítése" + "Ez a Matrix-azonosító nem található, ezért előfordulhat, hogy a meghívó nem érkezik meg." + "Szoba elhagyása" + "Világos" + "Hivatkozás a vágólapra másolva" + "Betöltés…" + "Üzenet" + "Üzenetműveletek" + "Üzenet elrendezése" + "Üzenet eltávolítva" + "Modern" + "Némítás" + "Nincs találat" + "Kapcsolat nélkül" + "Jelszó" + "Emberek" + "Állandó hivatkozás" + "Engedély" + "Összes szavazat: %1$s" + "Az eredmények a szavazás befejezése után jelennek meg" + "Adatvédelmi nyilatkozat" + "Reakció" + "Reakciók" + "Helyreállítási kulcs" + "Frissítés…" + "Válasz %1$s számára" + "Hiba jelentése" + "Probléma jelentése" + "A jelentés elküldve" + "Formázott szöveges szerkesztő" + "Szoba" + "Szoba neve" + "például a projekt neve" + "Képernyőzár" + "Személy keresése" + "Keresési találatok" + "Biztonság" + "Látta" + "Küldés…" + "A küldés sikertelen" + "Elküldve" + "A kiszolgáló nem támogatott" + "Kiszolgáló webcíme" + "Beállítások" + "Megosztott tartózkodási hely" + "Kijelentkezés" + "Csevegés megkezdése…" + "Matrica" + "Sikeres" + "Javaslatok" + "Szinkronizálás" + "Rendszer" + "Szöveg" + "Harmadik felek nyilatkozatai" + "Üzenetszál" + "Téma" + "Miről szól ez a szoba?" + "Nem lehet visszafejteni" + "Nem sikerült meghívót küldeni egy vagy több felhasználónak." + "Nem sikerült elküldeni a meghívót (meghívókat)" + "Feloldás" + "Némítás feloldása" + "Nem támogatott esemény" + "Felhasználónév" + "Az ellenőrzés megszakítva" + "Az ellenőrzés befejeződött" + "Videó" + "Hangüzenet" + "Várakozás…" + "Várakozás a visszafejtési kulcsra" + "Biztos, hogy befejezi ezt a szavazást?" + "Szavazás: %1$s" + "Eszköz ellenőrzése" + "Megerősítés" + "Figyelmeztetés" + "Nem sikerült létrehozni az állandó hivatkozást" + "Az %1$s nem tudta betölteni a térképet. Próbálja meg újra később." + "Nem sikerült betölteni az üzeneteket" + "Az %1$s nem tudta elérni a tartózkodási helyét. Próbálja meg újra később." + "Nem sikerült feltölteni a hangüzenetét." + "Az %1$snek nincs engedélye, hogy hozzáférjen a tartózkodási helyéhez. Ezt a beállításokban engedélyezheti." + "Az %1$snek nincs engedélye, hogy hozzáférjen a tartózkodási helyéhez. Engedélyezze alább az elérését." + "Az %1$snek nincs engedélye, hogy hozzáférjen a mikrofonjához. Engedélyezze, hogy tudjon hangüzenetet felvenni." + "Néhány üzenet nem került elküldésre" + "Elnézést, hiba történt" + "🔐️ Csatlakozz hozzám itt: %1$s" + "Beszélj velem az %1$s használatával: %2$s" + "%1$s Android" + + "%1$d megadott számjegy" + "%1$d megadott számjegy" + + + "Olvasta: %1$s és még %2$d felhasználó" + "Olvasta: %1$s és még %2$d felhasználó" + + + "%1$d tag" + "%1$d tag" + + + "%d szavazat" + "%d szavazat" + + "Az eszköz rázása a hibajelentéshez" + "Nem sikerült kiválasztani a médiát, próbálja újra." + "Nem sikerült feldolgozni a feltöltendő médiát, próbálja újra." + "Nem sikerült a média feltöltése, próbálja újra." + "Hely megosztása" + "Saját hely megosztása" + "Megnyitás az Apple Mapsben" + "Megnyitás a Google Mapsben" + "Megnyitás az OpenStreetMapen" + "E hely megosztása" + "Hely" + "Verzió: %1$s (%2$s)" + "hu" + "Hiba" + "Sikeres" + diff --git a/libraries/ui-strings/src/main/res/values-in/translations.xml b/libraries/ui-strings/src/main/res/values-in/translations.xml new file mode 100644 index 0000000000..f7f05f71f2 --- /dev/null +++ b/libraries/ui-strings/src/main/res/values-in/translations.xml @@ -0,0 +1,242 @@ + + + "Hapus" + "Sembunyikan kata sandi" + "Lompat ke bawah" + "Hanya sebutan" + "Dibisukan" + "Halaman %1$d" + "Jeda" + "Kolom PIN" + "Putar" + "Pemungutan suara" + "Pemungutan suara berakhir" + "Bereaksi dengan %1$s" + "Reaksi dengan emoji lain" + "Dibaca oleh %1$s dan %2$s" + "Dibaca oleh %1$s" + "Ketuk untuk melihat semua" + "Hapus reaksi dengan %1$s" + "Kirim berkas" + "Tampilkan kata sandi" + "Mulai panggilan" + "Menu pengguna" + "Rekam pesan suara." + "Berhenti merekam" + "Terima" + "Tambahkan ke lini masa" + "Kembali" + "Batal" + "Pilih foto" + "Hapus" + "Tutup" + "Selesaikan verifikasi" + "Konfirmasi" + "Lanjutkan" + "Salin" + "Salin tautan" + "Salin tautan ke pesan" + "Buat" + "Buat ruangan" + "Tolak" + "Hapus pemungutan suara" + "Nonaktifkan" + "Selesai" + "Sunting" + "Sunting pemungutan suara" + "Aktifkan" + "Akhiri pemungutan suara" + "Masukkan PIN" + "Lupa kata sandi?" + "Teruskan" + "Undang" + "Undang teman" + "Undang teman ke %1$s" + "Undang orang-orang ke %1$s" + "Undangan" + "Gabung" + "Pelajari lebih lanjut" + "Tinggalkan" + "Tinggalkan ruangan" + "Kelola akun" + "Kelola perangkat" + "Berikutnya" + "Tidak" + "Jangan sekarang" + "Oke" + "Pengaturan" + "Buka dengan" + "Balas cepat" + "Kutip" + "Bereaksi" + "Hapus" + "Balas" + "Balas dalam utas" + "Laporkan kutu" + "Laporkan Konten" + "Coba lagi" + "Coba dekripsi ulang" + "Simpan" + "Cari" + "Kirim" + "Kirim pesan" + "Bagikan" + "Bagikan tautan" + "Masuk lagi" + "Keluar dari akun" + "Keluar saja" + "Lewati" + "Mulai" + "Mulai obrolan" + "Mulai verifikasi" + "Ketuk untuk memuat peta" + "Ambil foto" + "Ketuk untuk opsi" + "Coba lagi" + "Tampilkan sumber" + "Ya" + "Muat lainnya" + "Tentang" + "Kebijakan penggunaan wajar" + "Pengaturan tingkat lanjut" + "Analitik" + "Penampilan" + "Audio" + "Gelembung" + "Pencadangan percakapan" + "Hak cipta" + "Membuat ruangan…" + "Keluar dari ruangan" + "Gelap" + "Kesalahan dekripsi" + "Opsi pengembang" + "Obrolan langsung" + "(disunting)" + "Penyuntingan" + "* %1$s %2$s" + "Enkripsi diaktifkan" + "Masukkan PIN Anda" + "Eror" + "Semua orang" + "Berkas" + "Berkas disimpan ke Unduhan" + "Teruskan pesan" + "GIF" + "Gambar" + "Membalas kepada %1$s" + "Pasang APK" + "ID Matrix ini tidak dapat ditemukan, sehingga undangan mungkin tidak diterima." + "Meninggalkan ruangan" + "Terang" + "Tautan disalin ke papan klip" + "Memuat…" + "Pesan" + "Tindakan pesan" + "Tata letak pesan" + "Pesan dihapus" + "Modern" + "Bisukan" + "Tidak ada hasil" + "Luring" + "Kata sandi" + "Orang" + "Tautan Permanen" + "Perizinan" + "Total suara: %1$s" + "Hasil akan terlihat setelah pemungutan suara berakhir" + "Kebijakan privasi" + "Reaksi" + "Reaksi" + "Kunci pemulihan" + "Menyegarkan…" + "Membalas %1$s" + "Laporkan kutu" + "Laporkan masalah" + "Laporan terkirim" + "Penyunting teks kaya" + "Ruangan" + "Nama ruangan" + "misalnya, nama proyek Anda" + "Layar kunci" + "Cari seseorang" + "Hasil pencarian" + "Keamanan" + "Dilihat oleh" + "Mengirim…" + "Pengiriman gagal" + "Terkirim" + "Server tidak didukung" + "URL Server" + "Pengaturan" + "Membagikan lokasi" + "Mengeluarkan dari akun" + "Memulai obrolan…" + "Stiker" + "Berhasil" + "Saran" + "Menyinkronkan" + "Sistem" + "Teks" + "Pemberitahuan pihak ketiga" + "Utas" + "Topik" + "Tentang apa ruangan ini?" + "Tidak dapat mendekripsi" + "Undangan tidak dapat dikirim ke satu atau beberapa pengguna." + "Tidak dapat mengirim undangan" + "Buka kunci" + "Bunyikan" + "Peristiwa tidak didukung" + "Nama pengguna" + "Verifikasi dibatalkan" + "Verifikasi selesai" + "Video" + "Pesan suara" + "Menunggu…" + "Menunggu pesan ini" + "Apakah Anda yakin ingin mengakhiri pemungutan suara ini?" + "Pemungutan suara: %1$s" + "Verifikasi perangkat" + "Konfirmasi" + "Peringatan" + "Gagal membuat tautan permanen" + "%1$s tidak dapat memuat peta. Silakan coba lagi nanti." + "Gagal memuat pesan" + "%1$s tidak dapat mengakses lokasi Anda. Silakan coba lagi nanti." + "Gagal mengunggah pesan suara Anda." + "%1$s tidak memiliki izin untuk mengakses lokasi Anda. Anda dapat mengaktifkan akses di Pengaturan." + "%1$s tidak memiliki izin untuk mengakses lokasi Anda. Aktifkan akses di bawah ini." + "%1$s tidak memiliki izin untuk mengakses mikrofon. Aktifkan akses untuk merekam pesan suara." + "Beberapa pesan belum terkirim" + "Maaf, terjadi kesalahan" + "🔐️ Bergabunglah dengan saya di %1$s" + "Hai, bicaralah dengan saya di %1$s: %2$s" + "%1$s Android" + + "%1$d digit dimasukkan" + + + "Dibaca oleh %1$s dan %2$d lainnya" + + + "%1$d anggota" + + + "%d suara" + + "Rageshake untuk melaporkan kutu" + "Gagal memilih media, silakan coba lagi." + "Gagal memproses media untuk diunggah, silakan coba lagi." + "Gagal mengunggah media, silakan coba lagi." + "Bagikan lokasi" + "Bagikan lokasi saya" + "Buka di Apple Maps" + "Buka di Google Maps" + "Buka di OpenStreetMap" + "Bagikan lokasi ini" + "Lokasi" + "Versi: %1$s (%2$s)" + "id" + "Eror" + "Berhasil" + diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml index 0b4700235b..b6576d988c 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -2,21 +2,27 @@ "Удалить" "Скрыть пароль" + "Перейти вниз" "Только упоминания" "Звук отключен" + "Страница %1$d" "Приостановить" "Поле PIN-кода" "Воспроизвести" "Опрос" "Опрос завершен" + "Реагировать вместе с %1$s" + "Реакция с помощью эмодзи" + "Прочитано %1$s и %2$s" "Прочитано %1$s" + "Нажмите, чтобы показать все" + "Удалить реакцию с %1$s" "Отправить файлы" "Показать пароль" "Начать звонок" "Меню пользователя" "Записать голосовое сообщение." "Остановить запись" - "Прочитано %1$s и %2$s" "Разрешить" "Добавить в хронологию" "Назад" @@ -33,9 +39,11 @@ "Создать" "Создать комнату" "Отклонить" + "Удалить опрос" "Отключить" "Готово" "Редактировать" + "Редактировать опрос" "Включить" "Завершить опрос" "Введите PIN-код" @@ -87,7 +95,7 @@ "Повторить попытку" "Показать источник" "Да" - "Редактировать опрос" + "Загрузить еще" "О приложении" "Политика допустимого использования" "Дополнительные параметры" @@ -102,6 +110,7 @@ "Темная" "Ошибка расшифровки" "Для разработчика" + "Прямой чат" "(изменено)" "Редактирование" "%1$s%2$s" @@ -123,7 +132,7 @@ "Загрузка…" "Сообщение" "Действия с сообщением" - "Оформление сообщений" + "Оформление сообщения" "Сообщение удалено" "Современный" "Без звука" diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index 7fe0ffd9d1..9b7db8c032 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -2,21 +2,27 @@ "Vymazať" "Skryť heslo" + "Prejsť na spodok" "Iba zmienky" "Stlmené" + "Strana %1$d" "Pozastaviť" "Pole PIN" "Prehrať" "Anketa" "Ukončená anketa" + "Reagovať s %1$s" + "Reagovať s inými emotikonmi" + "Prečítal/a %1$s a %2$s" "Prečítal/a %1$s" + "Ťuknutím zobrazíte všetko" + "Odstrániť reakciu s %1$s" "Odoslať súbory" "Zobraziť heslo" "Začať hovor" "Používateľské menu" "Nahrať hlasovú správu." "Zastaviť nahrávanie" - "Prečítal/a %1$s a %2$s" "Prijať" "Pridať na časovú os" "Späť" @@ -33,9 +39,11 @@ "Vytvoriť" "Vytvoriť miestnosť" "Odmietnuť" + "Odstrániť anketu" "Vypnúť" "Hotovo" "Upraviť" + "Upraviť anketu" "Povoliť" "Ukončiť anketu" "Zadajte PIN" @@ -87,7 +95,7 @@ "Skúste to znova" "Zobraziť zdroj" "Áno" - "Upraviť anketu" + "Načítať viac" "O aplikácii" "Zásady prijateľného používania" "Pokročilé nastavenia" @@ -102,6 +110,7 @@ "Tmavý" "Chyba dešifrovania" "Možnosti pre vývojárov" + "Priama konverzácia" "(upravené)" "Upravuje sa" "* %1$s %2$s" diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml index 6f6fa08892..2b3de76895 100644 --- a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml @@ -29,6 +29,7 @@ "停用" "完成" "編輯" + "編輯投票" "啟用" "結束投票" "忘記密碼?" @@ -74,7 +75,6 @@ "拍照" "檢視原始碼" "是" - "編輯投票" "關於" "可接受使用政策" "進階設定" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index c2f969551c..10d3bbd12e 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -2,21 +2,27 @@ "Delete" "Hide password" + "Jump to bottom" "Mentions only" "Muted" + "Page %1$d" "Pause" "PIN field" "Play" "Poll" "Ended poll" + "React with %1$s" + "React with other emojis" + "Read by %1$s and %2$s" "Read by %1$s" + "Tap to show all" + "Remove reaction with %1$s" "Send files" "Show password" "Start a call" "User menu" "Record voice message." "Stop recording" - "Read by %1$s and %2$s" "Accept" "Add to timeline" "Back" @@ -33,9 +39,11 @@ "Create" "Create a room" "Decline" + "Delete Poll" "Disable" "Done" "Edit" + "Edit poll" "Enable" "End poll" "Enter PIN" @@ -87,7 +95,7 @@ "Try again" "View source" "Yes" - "Edit poll" + "Load more" "About" "Acceptable use policy" "Advanced settings" @@ -102,6 +110,7 @@ "Dark" "Decryption error" "Developer options" + "Direct chat" "(edited)" "Editing" "* %1$s %2$s" diff --git a/libraries/usersearch/impl/build.gradle.kts b/libraries/usersearch/impl/build.gradle.kts index 226860e86f..f1d936f1c7 100644 --- a/libraries/usersearch/impl/build.gradle.kts +++ b/libraries/usersearch/impl/build.gradle.kts @@ -34,6 +34,7 @@ dependencies { implementation(projects.libraries.matrixui) implementation(projects.libraries.matrix.api) api(projects.libraries.usersearch.api) + implementation(libs.kotlinx.collections.immutable) testImplementation(libs.test.junit) testImplementation(libs.coroutines.test) diff --git a/libraries/usersearch/impl/src/test/kotlin/io/element/android/libraries/usersearch/impl/MatrixUserListDataSourceTest.kt b/libraries/usersearch/impl/src/test/kotlin/io/element/android/libraries/usersearch/impl/MatrixUserListDataSourceTest.kt index 793a9f7f58..6ec9f28658 100644 --- a/libraries/usersearch/impl/src/test/kotlin/io/element/android/libraries/usersearch/impl/MatrixUserListDataSourceTest.kt +++ b/libraries/usersearch/impl/src/test/kotlin/io/element/android/libraries/usersearch/impl/MatrixUserListDataSourceTest.kt @@ -16,7 +16,7 @@ package io.element.android.libraries.usersearch.impl -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults import io.element.android.libraries.matrix.api.user.MatrixUser @@ -25,6 +25,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_ID_2 import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.FakeMatrixClient +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.test.runTest import org.junit.Test @@ -37,7 +38,7 @@ internal class MatrixUserListDataSourceTest { searchTerm = "test", result = Result.success( MatrixSearchUserResults( - results = listOf( + results = persistentListOf( aMatrixUserProfile(), aMatrixUserProfile(userId = A_USER_ID_2) ), @@ -48,7 +49,7 @@ internal class MatrixUserListDataSourceTest { val dataSource = MatrixUserListDataSource(matrixClient) val results = dataSource.search("test", 2) - Truth.assertThat(results).containsExactly( + assertThat(results).containsExactly( aMatrixUserProfile(), aMatrixUserProfile(userId = A_USER_ID_2) ) @@ -64,7 +65,7 @@ internal class MatrixUserListDataSourceTest { val dataSource = MatrixUserListDataSource(matrixClient) val results = dataSource.search("test", 2) - Truth.assertThat(results).isEmpty() + assertThat(results).isEmpty() } @Test @@ -77,7 +78,7 @@ internal class MatrixUserListDataSourceTest { val dataSource = MatrixUserListDataSource(matrixClient) val result = dataSource.getProfile(A_USER_ID) - Truth.assertThat(result).isEqualTo(aMatrixUserProfile()) + assertThat(result).isEqualTo(aMatrixUserProfile()) } @Test @@ -90,7 +91,7 @@ internal class MatrixUserListDataSourceTest { val dataSource = MatrixUserListDataSource(matrixClient) val result = dataSource.getProfile(A_USER_ID) - Truth.assertThat(result).isNull() + assertThat(result).isNull() } private fun aMatrixUserProfile( diff --git a/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/di/VoiceRecorderModule.kt b/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/di/VoiceRecorderModule.kt index b21ab48ac3..29071a9879 100644 --- a/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/di/VoiceRecorderModule.kt +++ b/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/di/VoiceRecorderModule.kt @@ -21,6 +21,7 @@ import android.media.MediaRecorder import com.squareup.anvil.annotations.ContributesTo import dagger.Module import dagger.Provides +import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.di.RoomScope import io.element.android.libraries.voicerecorder.impl.audio.AudioConfig import io.element.android.libraries.voicerecorder.impl.audio.SampleRate @@ -50,7 +51,7 @@ object VoiceRecorderModule { VoiceFileConfig( cacheSubdir = "voice_recordings", fileExt = "ogg", - mimeType = "audio/ogg", + mimeType = MimeTypes.Ogg, ) @Provides diff --git a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImplTest.kt b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImplTest.kt index d67a44f6f3..030545a3bc 100644 --- a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImplTest.kt +++ b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImplTest.kt @@ -21,6 +21,7 @@ import android.media.MediaRecorder import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.appconfig.VoiceMessageConfig +import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.voicerecorder.api.VoiceRecorderState import io.element.android.libraries.voicerecorder.impl.audio.Audio import io.element.android.libraries.voicerecorder.impl.audio.AudioConfig @@ -85,7 +86,7 @@ class VoiceRecorderImplTest { assertThat(awaitItem()).isEqualTo( VoiceRecorderState.Finished( file = File(FILE_PATH), - mimeType = "audio/ogg", + mimeType = MimeTypes.Ogg, waveform = List(100) { 1f }, duration = VoiceMessageConfig.maxVoiceMessageDuration, ) @@ -107,7 +108,7 @@ class VoiceRecorderImplTest { assertThat(awaitItem()).isEqualTo( VoiceRecorderState.Finished( file = File(FILE_PATH), - mimeType = "audio/ogg", + mimeType = MimeTypes.Ogg, waveform = List(100) { 1f }, duration = 5.seconds, ) diff --git a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/audio/DBovAudioLevelCalculatorTest.kt b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/audio/DBovAudioLevelCalculatorTest.kt index cb5404d6f2..9aa3432ef6 100644 --- a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/audio/DBovAudioLevelCalculatorTest.kt +++ b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/audio/DBovAudioLevelCalculatorTest.kt @@ -16,7 +16,7 @@ package io.element.android.libraries.voicerecorder.impl.audio -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import org.junit.Test class DBovAudioLevelCalculatorTest { @@ -26,7 +26,7 @@ class DBovAudioLevelCalculatorTest { val calculator = DBovAudioLevelCalculator() val buffer = ShortArray(100) { Short.MAX_VALUE } val level = calculator.calculateAudioLevel(buffer) - Truth.assertThat(level).isEqualTo(1.0f) + assertThat(level).isEqualTo(1.0f) } @Test @@ -34,7 +34,7 @@ class DBovAudioLevelCalculatorTest { val calculator = DBovAudioLevelCalculator() val buffer = shortArrayOf(100, -200, 300, -400, 500, -600, 700, -800, 900, -1000) val level = calculator.calculateAudioLevel(buffer) - Truth.assertThat(level).apply { + assertThat(level).apply { isGreaterThan(0f) isLessThan(1f) } @@ -45,6 +45,6 @@ class DBovAudioLevelCalculatorTest { val calculator = DBovAudioLevelCalculator() val buffer = ShortArray(100) { 0 } val level = calculator.calculateAudioLevel(buffer) - Truth.assertThat(level).isEqualTo(0.0f) + assertThat(level).isEqualTo(0.0f) } } diff --git a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/audio/ResampleTest.kt b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/audio/ResampleTest.kt index 1edec86018..8413e4b244 100644 --- a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/audio/ResampleTest.kt +++ b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/audio/ResampleTest.kt @@ -16,26 +16,26 @@ package io.element.android.libraries.voicerecorder.impl.audio -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import org.junit.Test class ResampleTest { @Test fun `resample works`() { listOf(0.0f).resample(10).let { - Truth.assertThat(it).isEqualTo(listOf(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)) + assertThat(it).isEqualTo(listOf(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)) } listOf(1.0f).resample(10).let { - Truth.assertThat(it).isEqualTo(listOf(1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f)) + assertThat(it).isEqualTo(listOf(1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f)) } listOf(0.0f, 1.0f).resample(10).let { - Truth.assertThat(it).isEqualTo(listOf(0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f)) + assertThat(it).isEqualTo(listOf(0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f)) } listOf(0.0f, 0.5f, 1.0f).resample(10).let { - Truth.assertThat(it).isEqualTo(listOf(0.0f, 0.15f, 0.3f, 0.45000002f, 0.6f, 0.75f, 0.90000004f, 1.0f, 1.0f, 1.0f)) + assertThat(it).isEqualTo(listOf(0.0f, 0.15f, 0.3f, 0.45000002f, 0.6f, 0.75f, 0.90000004f, 1.0f, 1.0f, 1.0f)) } List(100) { it.toFloat() }.resample(10).let { - Truth.assertThat(it).isEqualTo(listOf(0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f)) + assertThat(it).isEqualTo(listOf(0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f)) } } } diff --git a/libraries/voicerecorder/test/build.gradle.kts b/libraries/voicerecorder/test/build.gradle.kts index 9628825380..1dc91ab108 100644 --- a/libraries/voicerecorder/test/build.gradle.kts +++ b/libraries/voicerecorder/test/build.gradle.kts @@ -28,4 +28,5 @@ dependencies { implementation(libs.coroutines.test) implementation(libs.test.truth) + implementation(projects.libraries.core) } diff --git a/libraries/voicerecorder/test/src/main/kotlin/io/element/android/libraries/voicerecorder/test/FakeVoiceRecorder.kt b/libraries/voicerecorder/test/src/main/kotlin/io/element/android/libraries/voicerecorder/test/FakeVoiceRecorder.kt index bc8bda59d1..c4cc1da0ae 100644 --- a/libraries/voicerecorder/test/src/main/kotlin/io/element/android/libraries/voicerecorder/test/FakeVoiceRecorder.kt +++ b/libraries/voicerecorder/test/src/main/kotlin/io/element/android/libraries/voicerecorder/test/FakeVoiceRecorder.kt @@ -17,6 +17,7 @@ package io.element.android.libraries.voicerecorder.test import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.voicerecorder.api.VoiceRecorder import io.element.android.libraries.voicerecorder.api.VoiceRecorderState import kotlinx.coroutines.flow.MutableStateFlow @@ -73,7 +74,7 @@ class FakeVoiceRecorder( null -> VoiceRecorderState.Idle else -> VoiceRecorderState.Finished( file = curRecording!!, - mimeType = "audio/ogg", + mimeType = MimeTypes.Ogg, duration = recordingDuration, waveform = waveform, ) diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 8dac5b7612..63988c218c 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -51,12 +51,12 @@ import org.gradle.jvm.toolchain.JavaLanguageVersion // Note: 2 digits max for each value private const val versionMajor = 0 -private const val versionMinor = 3 +private const val versionMinor = 4 // 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. -private const val versionPatch = 2 +private const val versionPatch = 0 object Versions { val versionCode = 4_000_000 + versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch diff --git a/plugins/src/main/kotlin/extension/CommonExtension.kt b/plugins/src/main/kotlin/extension/CommonExtension.kt index 97305dbc66..9e903e9057 100644 --- a/plugins/src/main/kotlin/extension/CommonExtension.kt +++ b/plugins/src/main/kotlin/extension/CommonExtension.kt @@ -18,10 +18,10 @@ package extension import Versions import com.android.build.api.dsl.CommonExtension +import org.gradle.accessors.dm.LibrariesForLibs import org.gradle.api.JavaVersion import org.gradle.api.Project import java.io.File -import org.gradle.accessors.dm.LibrariesForLibs fun CommonExtension<*, *, *, *, *>.androidConfig(project: Project) { defaultConfig { diff --git a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt index f6ec8b7cf1..625d2cf7dc 100644 --- a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt +++ b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt @@ -17,9 +17,9 @@ package extension import org.gradle.accessors.dm.LibrariesForLibs +import org.gradle.api.logging.Logger import org.gradle.kotlin.dsl.DependencyHandlerScope import org.gradle.kotlin.dsl.project -import org.gradle.api.logging.Logger import java.io.File private fun DependencyHandlerScope.implementation(dependency: Any) = dependencies.add("implementation", dependency) @@ -102,9 +102,11 @@ fun DependencyHandlerScope.allLibrariesImpl() { implementation(project(":libraries:mediaupload:impl")) implementation(project(":libraries:usersearch:impl")) implementation(project(":libraries:textcomposer:impl")) + implementation(project(":libraries:roomselect:impl")) implementation(project(":libraries:cryptography:impl")) implementation(project(":libraries:voicerecorder:impl")) implementation(project(":libraries:mediaplayer:impl")) + implementation(project(":libraries:mediaviewer:impl")) } fun DependencyHandlerScope.allServicesImpl() { diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt index a405167cbf..b8e529ad8e 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt @@ -26,13 +26,13 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.core.view.WindowCompat +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.impl.RustMatrixClientFactory import io.element.android.libraries.matrix.impl.auth.RustMatrixAuthenticationService import io.element.android.libraries.network.useragent.SimpleUserAgentProvider import io.element.android.libraries.sessionstorage.api.LoggedInState import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore -import io.element.android.libraries.theme.ElementTheme import io.element.android.services.toolbox.impl.systemclock.DefaultSystemClock import kotlinx.coroutines.runBlocking import java.io.File diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt index 1d74718aa9..7706cdbb5d 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt @@ -79,7 +79,6 @@ class RoomListScreen( lastMessageTimestampFormatter = DefaultLastMessageTimestampFormatter(dateTimeProvider, dateFormatters), roomLastMessageFormatter = DefaultRoomLastMessageFormatter( sp = stringProvider, - matrixClient = matrixClient, roomMembershipContentFormatter = RoomMembershipContentFormatter(matrixClient, stringProvider), profileChangeContentFormatter = ProfileChangeContentFormatter(stringProvider), stateContentFormatter = StateContentFormatter(stringProvider), diff --git a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PostHogFactory.kt b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PostHogFactory.kt index 40c72142c6..6c02b8b788 100644 --- a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PostHogFactory.kt +++ b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PostHogFactory.kt @@ -17,7 +17,9 @@ package io.element.android.services.analyticsproviders.posthog import android.content.Context -import com.posthog.android.PostHog +import com.posthog.PostHogInterface +import com.posthog.android.PostHogAndroid +import com.posthog.android.PostHogAndroidConfig import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.di.ApplicationContext import javax.inject.Inject @@ -27,31 +29,20 @@ class PostHogFactory @Inject constructor( private val buildMeta: BuildMeta, private val posthogEndpointConfigProvider: PosthogEndpointConfigProvider, ) { - - fun createPosthog(): PostHog { + fun createPosthog(): PostHogInterface { val endpoint = posthogEndpointConfigProvider.provide() - return PostHog.Builder(context, endpoint.apiKey, endpoint.host) - // Record certain application events automatically! (off/false by default) - // .captureApplicationLifecycleEvents() - // Record screen views automatically! (off/false by default) - // .recordScreenViews() - // Capture deep links as part of the screen call. (off by default) - // .captureDeepLinks() - // Maximum number of events to keep in queue before flushing (default 20) - // .flushQueueSize(20) - // Max delay before flushing the queue (30 seconds) - // .flushInterval(30, TimeUnit.SECONDS) - // Enable or disable collection of ANDROID_ID (true) - .collectDeviceId(false) - .logLevel(getLogLevel()) - .build() - } - - private fun getLogLevel(): PostHog.LogLevel { - return if (buildMeta.isDebuggable) { - PostHog.LogLevel.DEBUG - } else { - PostHog.LogLevel.INFO - } + return PostHogAndroid.with( + context, + PostHogAndroidConfig( + apiKey = endpoint.apiKey, + host = endpoint.host, + captureApplicationLifecycleEvents = false, + captureDeepLinks = false, + captureScreenViews = false, + ).also { + it.debug = buildMeta.isDebuggable + it.sendFeatureFlagEvent = false + } + ) } } diff --git a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt index e371c6fef4..16cac5722d 100644 --- a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt +++ b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt @@ -16,8 +16,7 @@ package io.element.android.services.analyticsproviders.posthog -import com.posthog.android.PostHog -import com.posthog.android.Properties +import com.posthog.PostHogInterface import com.squareup.anvil.annotations.ContributesMultibinding import im.vector.app.features.analytics.itf.VectorAnalyticsEvent import im.vector.app.features.analytics.itf.VectorAnalyticsScreen @@ -37,30 +36,31 @@ class PosthogAnalyticsProvider @Inject constructor( ) : AnalyticsProvider { override val name = "Posthog" - private var posthog: PostHog? = null + private var posthog: PostHogInterface? = null private var analyticsId: String? = null override fun init() { posthog = createPosthog() - posthog?.optOut(false) + posthog?.optIn() + // Timber.e("PostHog distinctId: ${posthog?.distinctId()}") identifyPostHog() } override fun stop() { // When opting out, ensure that the queue is flushed first, or it will be flushed later (after user has revoked consent) posthog?.flush() - posthog?.optOut(true) - posthog?.shutdown() + posthog?.optOut() + posthog?.close() posthog = null analyticsId = null } override fun capture(event: VectorAnalyticsEvent) { - posthog?.capture(event.getName(), event.getProperties()?.toPostHogProperties()) + posthog?.capture(event.getName(), properties = event.getProperties()?.keepOnlyNonNullValues()) } override fun screen(screen: VectorAnalyticsScreen) { - posthog?.screen(screen.getName(), screen.getProperties()?.toPostHogProperties()) + posthog?.screen(screen.getName(), properties = screen.getProperties()) } override fun updateUserProperties(userProperties: UserProperties) { @@ -74,7 +74,7 @@ class PosthogAnalyticsProvider @Inject constructor( // Not implemented } - private fun createPosthog(): PostHog = postHogFactory.createPosthog() + private fun createPosthog(): PostHogInterface = postHogFactory.createPosthog() private fun identifyPostHog() { val id = analyticsId ?: return @@ -86,24 +86,15 @@ class PosthogAnalyticsProvider @Inject constructor( // posthog?.identify(id, lateInitUserPropertiesFactory.createUserProperties()?.getProperties()?.toPostHogUserProperties(), IGNORED_OPTIONS) } } - - private fun Map?.toPostHogProperties(): Properties? { - if (this == null) return null - - return Properties().apply { - putAll(this@toPostHogProperties) - } - } - - /** - * We avoid sending nulls as part of the UserProperties as this will reset the values across all devices. - * The UserProperties event has nullable properties to allow for clients to opt in. - */ - /* - private fun Map.toPostHogUserProperties(): Properties { - return Properties().apply { - putAll(this@toPostHogUserProperties.filter { it.value != null }) - } - } - */ +} + +private fun Map.keepOnlyNonNullValues(): Map { + val result = mutableMapOf() + for (entry in this) { + val value = entry.value + if (value != null) { + result[entry.key] = value + } + } + return result } diff --git a/services/appnavstate/test/src/main/kotlin/io/element/android/services/appnavstate/test/FakeAppNavigationStateService.kt b/services/appnavstate/test/src/main/kotlin/io/element/android/services/appnavstate/test/FakeAppNavigationStateService.kt index a09e2a9c5e..ac6060fa8b 100644 --- a/services/appnavstate/test/src/main/kotlin/io/element/android/services/appnavstate/test/FakeAppNavigationStateService.kt +++ b/services/appnavstate/test/src/main/kotlin/io/element/android/services/appnavstate/test/FakeAppNavigationStateService.kt @@ -20,23 +20,19 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SpaceId import io.element.android.libraries.matrix.api.core.ThreadId -import io.element.android.services.appnavstate.api.NavigationState -import io.element.android.services.appnavstate.api.AppNavigationStateService import io.element.android.services.appnavstate.api.AppNavigationState +import io.element.android.services.appnavstate.api.AppNavigationStateService +import io.element.android.services.appnavstate.api.NavigationState import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow class FakeAppNavigationStateService( - private val fakeAppNavigationState: MutableStateFlow = MutableStateFlow( + override val appNavigationState: MutableStateFlow = MutableStateFlow( AppNavigationState( navigationState = NavigationState.Root, isInForeground = true, ) ), ) : AppNavigationStateService { - - override val appNavigationState: StateFlow = fakeAppNavigationState - override fun onNavigateToSession(owner: String, sessionId: SessionId) = Unit override fun onLeavingSession(owner: String) = Unit diff --git a/services/toolbox/test/src/main/kotlin/io/element/android/services/toolbox/test/strings/FakeStringProvider.kt b/services/toolbox/test/src/main/kotlin/io/element/android/services/toolbox/test/strings/FakeStringProvider.kt new file mode 100644 index 0000000000..c9f9a84fc9 --- /dev/null +++ b/services/toolbox/test/src/main/kotlin/io/element/android/services/toolbox/test/strings/FakeStringProvider.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 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 io.element.android.services.toolbox.test.strings + +import io.element.android.services.toolbox.api.strings.StringProvider + +class FakeStringProvider( + private val defaultResult: String = "A string" +) : StringProvider { + override fun getString(resId: Int): String { + return defaultResult + } + + override fun getString(resId: Int, vararg formatArgs: Any?): String { + return defaultResult + } + + override fun getQuantityString(resId: Int, quantity: Int, vararg formatArgs: Any?): String { + return defaultResult + } +} diff --git a/services/toolbox/test/src/main/kotlin/io/element/android/services/toolbox/test/systemclock/FakeSystemClock.kt b/services/toolbox/test/src/main/kotlin/io/element/android/services/toolbox/test/systemclock/FakeSystemClock.kt new file mode 100644 index 0000000000..64d631c411 --- /dev/null +++ b/services/toolbox/test/src/main/kotlin/io/element/android/services/toolbox/test/systemclock/FakeSystemClock.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 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 io.element.android.services.toolbox.test.systemclock + +import io.element.android.services.toolbox.api.systemclock.SystemClock + +const val A_FAKE_TIMESTAMP = 123L + +class FakeSystemClock : SystemClock { + override fun epochMillis(): Long { + return A_FAKE_TIMESTAMP + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 9ac2c96dde..cd3689abe3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -79,3 +79,12 @@ fun includeProjects(directory: File, path: String, maxDepth: Int = 1) { includeProjects(File(rootDir, "features"), ":features") includeProjects(File(rootDir, "libraries"), ":libraries") includeProjects(File(rootDir, "services"), ":services") + +// Uncomment to include the compound-android module as a local dependency so you can work on it locally. +// You will also need to clone it in the specified folder. +// includeBuild("checkouts/compound-android") { +// dependencySubstitution { +// // substitute remote dependency with local module +// substitute(module("io.element.android:compound-android")).using(project(":compound")) +// } +// } diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistComposableTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistComposableTest.kt index 9eb23cfc61..e3c29e8dd1 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistComposableTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistComposableTest.kt @@ -24,6 +24,7 @@ import com.lemonappdev.konsist.api.ext.list.withAllAnnotationsOf import com.lemonappdev.konsist.api.ext.list.withTopLevel import com.lemonappdev.konsist.api.ext.list.withoutName import com.lemonappdev.konsist.api.ext.list.withoutNameEndingWith +import com.lemonappdev.konsist.api.ext.list.withoutReceiverType import com.lemonappdev.konsist.api.verify.assertTrue import org.junit.Test @@ -37,6 +38,7 @@ class KonsistComposableTest { .withoutModifier(KoModifier.PRIVATE) .withoutNameEndingWith("Preview") .withAllAnnotationsOf(Composable::class) + .withoutReceiverType() .withoutName( // Add some exceptions... "OutlinedButton", diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistTestTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistTestTest.kt index 5f7acf8d39..e85d95650d 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistTestTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistTestTest.kt @@ -20,6 +20,8 @@ import com.lemonappdev.konsist.api.Konsist import com.lemonappdev.konsist.api.ext.list.modifierprovider.withoutOverrideModifier import com.lemonappdev.konsist.api.ext.list.withFunction import com.lemonappdev.konsist.api.ext.list.withReturnType +import com.lemonappdev.konsist.api.ext.list.withoutName +import com.lemonappdev.konsist.api.verify.assertFalse import com.lemonappdev.konsist.api.verify.assertTrue import org.junit.Test @@ -46,4 +48,66 @@ class KonsistTestTest { functionDeclaration.name == "create${functionDeclaration.returnType?.name}" } } + + @Test + fun `assertion methods must be imported`() { + Konsist + .scopeFromTest() + .functions() + // Exclude self + .withoutName("assertion methods must be imported") + .assertFalse( + additionalMessage = "Import methods from Truth, instead of using for instance Truth.assertThat(...)" + ) { functionDeclaration -> + functionDeclaration.text.contains("Truth.") + } + } + + @Test + fun `use isFalse() instead of isEqualTo(false)`() { + Konsist + .scopeFromTest() + .functions() + // Exclude self + .withoutName("use isFalse() instead of isEqualTo(false)") + .assertFalse { functionDeclaration -> + functionDeclaration.text.contains("isEqualTo(false)") + } + } + + @Test + fun `use isTrue() instead of isEqualTo(true)`() { + Konsist + .scopeFromTest() + .functions() + // Exclude self + .withoutName("use isTrue() instead of isEqualTo(true)") + .assertFalse { functionDeclaration -> + functionDeclaration.text.contains("isEqualTo(true)") + } + } + + @Test + fun `use isEmpty() instead of isEqualTo(empty)`() { + Konsist + .scopeFromTest() + .functions() + // Exclude self + .withoutName("use isEmpty() instead of isEqualTo(empty)") + .assertFalse { functionDeclaration -> + functionDeclaration.text.contains("isEqualTo(empty") + } + } + + @Test + fun `use isNull() instead of isEqualTo(null)`() { + Konsist + .scopeFromTest() + .functions() + // Exclude self + .withoutName("use isNull() instead of isEqualTo(null)") + .assertFalse { functionDeclaration -> + functionDeclaration.text.contains("isEqualTo(null)") + } + } } diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt index 3e47dd63ce..0c8bd89951 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt @@ -19,7 +19,7 @@ package io.element.android.tests.testutils import app.cash.turbine.Event import app.cash.turbine.ReceiveTurbine import app.cash.turbine.withTurbineTimeout -import io.element.android.libraries.core.data.tryOrNull +import io.element.android.libraries.core.bool.orFalse import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds @@ -29,7 +29,7 @@ import kotlin.time.Duration.Companion.milliseconds * @return the list of consumed items. */ suspend fun ReceiveTurbine.consumeItemsUntilTimeout(timeout: Duration = 100.milliseconds): List { - return consumeItemsUntilPredicate(timeout) { false } + return consumeItemsUntilPredicate(timeout, ignoreTimeoutError = true) { false } } /** @@ -49,22 +49,29 @@ suspend fun ReceiveTurbine.awaitLastSequentialItem(): T { */ suspend fun ReceiveTurbine.consumeItemsUntilPredicate( timeout: Duration = 100.milliseconds, + ignoreTimeoutError: Boolean = false, predicate: (T) -> Boolean, ): List { val items = ArrayList() - tryOrNull { - var foundItemOrFinished = false - while (!foundItemOrFinished) { + var exitLoop = false + try { + while (!exitLoop) { when (val event = withTurbineTimeout(timeout) { awaitEvent() }) { is Event.Item -> { items.add(event.value) - if (predicate(event.value)) { - foundItemOrFinished = true - } + exitLoop = predicate(event.value) } - Event.Complete, is Event.Error -> foundItemOrFinished = true + Event.Complete -> error("Unexpected complete") + is Event.Error -> throw event.throwable } } + } catch (assertionError: AssertionError) { + // TurbineAssertionError is internal :/, so rely on the message + if (assertionError.message?.startsWith("No value produced in").orFalse() && ignoreTimeoutError) { + // Timeout, ignore + } else { + throw assertionError + } } return items } diff --git a/tests/uitests/src/test/kotlin/ui/S.kt b/tests/uitests/src/test/kotlin/ui/S.kt index 0aaf234a7f..0cfb95129c 100644 --- a/tests/uitests/src/test/kotlin/ui/S.kt +++ b/tests/uitests/src/test/kotlin/ui/S.kt @@ -40,7 +40,7 @@ import com.android.ide.common.rendering.api.SessionParams import com.android.resources.NightMode import com.google.testing.junit.testparameterinjector.TestParameter import com.google.testing.junit.testparameterinjector.TestParameterInjector -import io.element.android.libraries.theme.ElementTheme +import io.element.android.compound.theme.ElementTheme import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_LoadingRoomNodeView_null_LoadingRoomNodeView-Day-2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_LoadingRoomNodeView_null_LoadingRoomNodeView-Day-2_2_null_0,NEXUS_5,1.0,en].png index e0a91a62b7..3c0e2e01a0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_LoadingRoomNodeView_null_LoadingRoomNodeView-Day-2_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_LoadingRoomNodeView_null_LoadingRoomNodeView-Day-2_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa1ddb8a63e7b348861732be8c79cacf136b84b1316670dc511947b8794582eb +oid sha256:476e75b7c2ad574247248e335b24398602143574e3161c7c13e0c68f49eaa4c5 size 9707 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_LoadingRoomNodeView_null_LoadingRoomNodeView-Day-2_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_LoadingRoomNodeView_null_LoadingRoomNodeView-Day-2_2_null_1,NEXUS_5,1.0,en].png index 259dd7ce8c..2262b49b3b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_LoadingRoomNodeView_null_LoadingRoomNodeView-Day-2_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_LoadingRoomNodeView_null_LoadingRoomNodeView-Day-2_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df78caa663ea94758cf16c70fcce66f5af7bb0523e18e25c1a5a131e41687b08 -size 11920 +oid sha256:248c560df915df6da94985f6bfa30cc455f5dde66d8ba5d6e1f7e21d7a72f6ba +size 11923 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_LoadingRoomNodeView_null_LoadingRoomNodeView-Night-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_LoadingRoomNodeView_null_LoadingRoomNodeView-Night-2_3_null_0,NEXUS_5,1.0,en].png index 3518efa14f..ad4b66c6b1 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_LoadingRoomNodeView_null_LoadingRoomNodeView-Night-2_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_LoadingRoomNodeView_null_LoadingRoomNodeView-Night-2_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be5d601d56794ba4f707584bc6f9c7d76c1e2341da69e7a8dd0fe3224388a7b5 -size 9237 +oid sha256:49355fd9a91f3715c7a25085da9d6de771cb0ded84127e85942a20da59ef7806 +size 9261 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_LoadingRoomNodeView_null_LoadingRoomNodeView-Night-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_LoadingRoomNodeView_null_LoadingRoomNodeView-Night-2_3_null_1,NEXUS_5,1.0,en].png index 85ef954246..681ec75141 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_LoadingRoomNodeView_null_LoadingRoomNodeView-Night-2_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_LoadingRoomNodeView_null_LoadingRoomNodeView-Night-2_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ada59e4a20323a9485e4c7dae97a8d53c9736e28e099545229cd9845c6496f3 -size 11336 +oid sha256:9218d6e8acd136d8258116b2dc05de7e22ce5f901f4a3a654ff40300a905edf7 +size 11355 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[compound.theme_ColorsSchemeDark_null_ColorsSchemeDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[compound.theme_ColorsSchemeDark_null_ColorsSchemeDark_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..27e047fa66 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[compound.theme_ColorsSchemeDark_null_ColorsSchemeDark_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d3f14d6a81aa448bd12479078f8f46c7e4b540a4cb38eed7465bccb23066b76 +size 93476 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[compound.theme_ColorsSchemeLight_null_ColorsSchemeLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[compound.theme_ColorsSchemeLight_null_ColorsSchemeLight_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..93baa5532a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[compound.theme_ColorsSchemeLight_null_ColorsSchemeLight_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21f14167693d1e9f05b2aa4b8d073fad054c101ddce699c19a772d58f81f7f43 +size 88577 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Day-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Day-0_0_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index bf9fd3aea2..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Day-0_0_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dbd443a41c50072013892b4db0592d465862a087676f5ccc7270e1f77601f822 -size 50696 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Day-0_1_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ef4f316ea1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Day-0_1_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c6664fe1c30fee448945a238795a60f4ddbc8fefec0bc7e48e9345e6b5b429e5 +size 50702 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Night-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Night-0_1_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index bdf8633954..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Night-0_1_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0d9828a527145ccb1778d56327c4b709ff57ca9be415ced7bdf5dbfa605e2828 -size 49541 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Night-0_2_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..34a414aed1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_AnalyticsOptInView_null_AnalyticsOptInView-Night-0_2_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91e2590fa78c1dbc1a24eb13602df844259b5c9e7a14fd0d3cfbf2d9086e0453 +size 49543 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.call.ui_CallScreenView_null_CallScreenView-Day-0_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.call.ui_CallScreenView_null_CallScreenView-Day-0_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index f44d866ff8..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.call.ui_CallScreenView_null_CallScreenView-Day-0_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a28b7969455f17784f060291ce58b3720324baa67e9d93c2aa59f6d979268678 -size 14429 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.call.ui_CallScreenView_null_CallScreenView-Day-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.call.ui_CallScreenView_null_CallScreenView-Day-0_1_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a6e4a4c4bd --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.call.ui_CallScreenView_null_CallScreenView-Day-0_1_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:46ecef8831b34f3e53231475ef4bbd2620800d5c5ee72e271228c0cfacae210c +size 14475 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.call.ui_CallScreenView_null_CallScreenView-Night-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.call.ui_CallScreenView_null_CallScreenView-Night-0_1_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 232a3e752a..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.call.ui_CallScreenView_null_CallScreenView-Night-0_1_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f8a811677a50035a361f65ece4a1281346423226db6a1b8b3b8611f6b2f1d23d -size 13099 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.call.ui_CallScreenView_null_CallScreenView-Night-0_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.call.ui_CallScreenView_null_CallScreenView-Night-0_2_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e0e37795c6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.call.ui_CallScreenView_null_CallScreenView-Night-0_2_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c78c7e11f3f6409e603ef0711f6828c2361ba8f20c54be6e382f72d936b31c52 +size 13159 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Day-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Day-0_0_null_0,NEXUS_5,1.0,en].png index ee1a9f7134..bb770c3025 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Day-0_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Day-0_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:280ebbe16a131cb4db1632898d58f78a80af7bc83d2c4aa4396f9af35851d4ce -size 15377 +oid sha256:8911273d36817eb378fca037a4b38067cc7cd92615574387482e054a61343aa4 +size 15394 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Day-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Day-0_0_null_1,NEXUS_5,1.0,en].png index e408b763cd..003c6e5c5a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Day-0_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Day-0_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:05d6cc8105f90ad7d3376239cc8851a422ca24002f2fd37db1357e869abd5b41 -size 30263 +oid sha256:bab7fb66e5d635bf44dbc14b7777a0640bc5142f05a1ed90fa1759dbefde783d +size 30284 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Day-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Day-0_0_null_2,NEXUS_5,1.0,en].png index 6ee48072cb..c624c24642 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Day-0_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Day-0_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e9bff1424c5aaef2b4638a25878bbadd8c5267b32ffc4a504ce28e90016338bb -size 83711 +oid sha256:2fb09eea80f2b2fa088abf939e0b86005850912eda57fc3348f6ec3cd8e881cf +size 83716 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Night-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Night-0_1_null_0,NEXUS_5,1.0,en].png index 840b6c8bd2..6982a6c989 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Night-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Night-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:541c5823f11bbf4bcf6061a479f7d491dddd7004d2328e6bb0c38d2367a8c42f -size 14270 +oid sha256:f17f37be980b76c6993b8638707e042664e94cd05c185865d0c60c525b6357c1 +size 14289 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Night-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Night-0_1_null_1,NEXUS_5,1.0,en].png index e25d694596..cbbe757be4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Night-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Night-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78b011770254431118a24382c9892fa9163b6d5692cbe7a38543eae549771221 -size 28809 +oid sha256:39cf31ec4b57a315a3f31500c0fb372facf8fa7f7afb869c9f6ae2ef7512d823 +size 28816 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Night-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Night-0_1_null_2,NEXUS_5,1.0,en].png index bafd6e8f29..9c252c2e32 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Night-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_AddPeopleView_null_AddPeopleView-Night-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:832862848001c1e672872607b03967388e29345589157140dd26f50557874cf5 -size 81005 +oid sha256:28062b0a65a24453a3163dfd05a4ddb55e72f56c97c5bcf51337a6012bfaa76a +size 81006 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_RoomPrivacyOption_null_RoomPrivacyOption-Day-1_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_RoomPrivacyOption_null_RoomPrivacyOption-Day-1_1_null,NEXUS_5,1.0,en].png index 71a2028f03..f8be40a260 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_RoomPrivacyOption_null_RoomPrivacyOption-Day-1_1_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_RoomPrivacyOption_null_RoomPrivacyOption-Day-1_1_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f332381c7d390fd7b18b3ffc802d688add498afc7dc7fc597fa226440dfa8253 -size 35649 +oid sha256:711a46b6c33e0d98d41fb6e85278bd9b9d1dc5ce4032b5c994fc4eabfbd825b0 +size 35642 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_RoomPrivacyOption_null_RoomPrivacyOption-Night-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_RoomPrivacyOption_null_RoomPrivacyOption-Night-1_2_null,NEXUS_5,1.0,en].png index 655e013467..484c6156b7 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_RoomPrivacyOption_null_RoomPrivacyOption-Night-1_2_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_RoomPrivacyOption_null_RoomPrivacyOption-Night-1_2_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ed6444c7bfcf9a64fe239e04d2cfb3023f6023b3ec78fc7bc1a27b7ca78ba78 -size 33606 +oid sha256:6caff449d938cfdcdb0e8bd8bac6d03e66bf6e18be6bb5321b990b5517e0c4b4 +size 33602 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_SearchMultipleUsersResultItem_null_SearchMultipleUsersResultItem_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_SearchMultipleUsersResultItem_null_SearchMultipleUsersResultItem_0_null,NEXUS_5,1.0,en].png index 047877ab68..2bd50fb9bc 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_SearchMultipleUsersResultItem_null_SearchMultipleUsersResultItem_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_SearchMultipleUsersResultItem_null_SearchMultipleUsersResultItem_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc5058851b3ed2605906ea27916af6dc07c518e485b815d577295ad2cb965c63 +oid sha256:748900111b5b9cb1235f979d889098366ed66a88e8078224ec2780feac47a2e8 size 86365 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_SearchSingleUserResultItem_null_SearchSingleUserResultItem_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_SearchSingleUserResultItem_null_SearchSingleUserResultItem_0_null,NEXUS_5,1.0,en].png index e51d18ae64..2d75e418bf 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_SearchSingleUserResultItem_null_SearchSingleUserResultItem_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_SearchSingleUserResultItem_null_SearchSingleUserResultItem_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d75c2c82789f2fbeb90d64cb3c582f25cc1987bc8f066ae2bae9783af076d5b -size 45295 +oid sha256:4b1fd11da79f083f6fe06c9141d50e143234bd2f8da8a4a0cc4896072675411f +size 45294 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_0,NEXUS_5,1.0,en].png index b281254e41..92b5dcf611 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd03b2dbb40ab82f6775ed36d198635121f2be32b4160fdc8905afbf9eb1d15e -size 10520 +oid sha256:c2e0df56f1b1d1b72722d2d91a82bd367012b64c1223dd6eed4bfbe49ec59f1b +size 10530 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_1,NEXUS_5,1.0,en].png index 10f174d7e9..428ad5ca2a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f9d563fa3bd6f84f13908b6598b66185debc8abe15b567998defd8fdf6b8060 -size 26202 +oid sha256:b91ef02cb812278df3508d4fd07feff85ab33513553e28dc52953e93abc56fbc +size 26205 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_2,NEXUS_5,1.0,en].png index 4b8c7c51f3..b44f30cb4d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:99757e5f48eeea2b5797d1379d9d50886cc6f950c1ce1557a03e06295e5b4209 -size 8875 +oid sha256:ad9ec56dd85e7dce08a64b022fa2271c1416f6c27e4eb0d80200bb073de60036 +size 8870 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_3,NEXUS_5,1.0,en].png index e6fb56e1b3..d81d496d15 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1180179773f57495767d85d3b97e5923a75f773156b661abffa51ee280325164 -size 7602 +oid sha256:4731f36d3ee1331c0767ad27d4286feca266deabfd55960a89b0e823925b30f2 +size 7646 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_4,NEXUS_5,1.0,en].png index e6fb56e1b3..d81d496d15 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1180179773f57495767d85d3b97e5923a75f773156b661abffa51ee280325164 -size 7602 +oid sha256:4731f36d3ee1331c0767ad27d4286feca266deabfd55960a89b0e823925b30f2 +size 7646 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_5,NEXUS_5,1.0,en].png index f44d0b3e45..e6ee409e1f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d0f2433ecde91f665826dc0beae0900ed75d10ad5d7290b637b3256a4164212f -size 64623 +oid sha256:62e070d458f0b9fb2ef7d8d541c8a572ffc5b69be81f301e7a06dce61fc91482 +size 64643 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_6,NEXUS_5,1.0,en].png index 629b9c5545..2cb0696469 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b144041542e179406dcc674e43344c32dae3ba540102d26c5b82b7258fef434 -size 69289 +oid sha256:82927ae74dd1be009f61d3c0be5a4a9e9109c01b12d284ed69225e9edf1b5492 +size 69302 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_7,NEXUS_5,1.0,en].png index 81f59e0fe6..13fffb08c2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Day-2_2_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8ee231d6ded9b516126f0893a475ab08694aeaa1e073caa7fcc0ec57531358a -size 12368 +oid sha256:29f5f872f5e4ff3ef4057253df3eb169e1cdd3b8c3a2854c3a6df1642535115e +size 12393 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_0,NEXUS_5,1.0,en].png index 82d6a9c88d..0637ae79f9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72fd14733980344161ea94155f6d50e037a88a01393ec0e10e5df7ee8392dece -size 9971 +oid sha256:9685e367002edc9cde5118bbf2febb63b78faafa29416e4d5c3adb97c0e3a4c9 +size 9978 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_1,NEXUS_5,1.0,en].png index dee7827b58..b61994b3f2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a639ce5cd7a796f35c9436cc5a1efeba12e0480cdad655778850788f01827de -size 25436 +oid sha256:f3d5874f77ba4365401712745c998b49da3f0fb87fcf568814f3a6c0a35cab7b +size 25439 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_2,NEXUS_5,1.0,en].png index 857bd27b17..4b8056e210 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55167302862c369542fd6541bd376e28aaadfd72d45f1d0a41c961bc020d3302 -size 8622 +oid sha256:1e79ddfc0d0b6bf364e882a28cda0407588fdc81e5e1edc4d39283e7bf9aa834 +size 8623 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_3,NEXUS_5,1.0,en].png index afad0f4668..573ab912c2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4eb4cb9406ff5afa7f562782e221a07397858ba05beb000e62cc4608d02e24c -size 7204 +oid sha256:ae2119fa8d67dc6787a3cf86ada1259a4ff941d1d9a55936d5ee84e71e42ac72 +size 7217 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_4,NEXUS_5,1.0,en].png index afad0f4668..573ab912c2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4eb4cb9406ff5afa7f562782e221a07397858ba05beb000e62cc4608d02e24c -size 7204 +oid sha256:ae2119fa8d67dc6787a3cf86ada1259a4ff941d1d9a55936d5ee84e71e42ac72 +size 7217 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_5,NEXUS_5,1.0,en].png index 6cbec0c506..2d8c37b456 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0404c3ed85057f4fa74a89ca81b69a82f00823fbcb1e3dc169fb0f45fd467cfa -size 63775 +oid sha256:bcd95bb389f970f1981990b0db1ccdcd188f1e997ef75ee526d9a211ad89d81d +size 63806 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_6,NEXUS_5,1.0,en].png index c3651eff14..c2e3c732f1 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b1708591dcdbfbd1e9df6742f3eef62d033b5026c87b965639ed37c99aa6b09 -size 67979 +oid sha256:7732066b2264c118ddcc6b01c01279303c6e9f94e3c88b2e9dd8b0aef75620d9 +size 68006 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_7,NEXUS_5,1.0,en].png index f0d073a0a1..b89888f383 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_UserListView_null_UserListView-Night-2_3_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f96ce0b4f5b2ae5f1aec3fc9594d0430762e25bfb4305b1af73167228050e8d1 -size 11560 +oid sha256:deee6be545672348d4cd04921d69cbb8a83dfb23f0378df8ab564f536682de3b +size 11565 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Day-3_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Day-3_3_null_0,NEXUS_5,1.0,en].png index 456c1b8a2b..53321022be 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Day-3_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Day-3_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8931b6e86b7eaa41de10c13259b5b0da5e34837204191f1258b968d6814f04b5 -size 61157 +oid sha256:d12c8d2f73c4e0c0a26bc07b25e03d0375cf1b48a9e3ddb1b96e2f558e9c4152 +size 60435 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Day-3_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Day-3_3_null_1,NEXUS_5,1.0,en].png index c872252b2e..647d83fa5d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Day-3_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Day-3_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f331af0d2dd34f4afef67a23e1b47d41bb31971fbe18752c61225a280f7f06f -size 84819 +oid sha256:9e597d8739ebdddfedd8ac9246534cbebd23808be4d12033707d570b911c56b0 +size 84102 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Night-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Night-3_4_null_0,NEXUS_5,1.0,en].png index 28eeec5424..d0e3ad6131 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Night-3_4_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Night-3_4_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3e39f1f6e0a32bba634c12e7b58aa11eb45081575f2f348d6da701973bafa176 -size 57718 +oid sha256:cde8dbcb20123a622eb761f2229c1bec46e37cf0069e07032f7d10ad16e64d06 +size 57185 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Night-3_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Night-3_4_null_1,NEXUS_5,1.0,en].png index 03d4d04550..aa2bbae55f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Night-3_4_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Night-3_4_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:57a2b92097bf41a2ef79c9f6f41ecca1d1b631fe15377458df2a5bd27165caa0 -size 81467 +oid sha256:08fb1bd27537a4ad4bbf4abd345f20391d8ed23cd51da10547ca3aee5ebf1510 +size 80922 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Day-4_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Day-4_4_null_0,NEXUS_5,1.0,en].png index 26a31135df..3b6d8bc1b0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Day-4_4_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Day-4_4_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79f771e31445db99b25df34ae686a940d0bb7e44745abe5ed2da79bf258122ed -size 23090 +oid sha256:e055da42d13817ccd4f6de3a45ac24db3a64aaf58e6f43e66f362ce391873100 +size 23794 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Day-4_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Day-4_4_null_1,NEXUS_5,1.0,en].png index ddd84aa690..2f6f05515a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Day-4_4_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Day-4_4_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6da12bea6f0b282922ab2a8860a213aa6af2da49b72b11528ab8cb177ba6b11b -size 23676 +oid sha256:7ef94e1e06aba0e06550f6ecb7abac1754f0b00efd68ff068773742534710909 +size 23687 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Day-4_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Day-4_4_null_2,NEXUS_5,1.0,en].png index 904b4e0e74..b03e9bd149 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Day-4_4_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Day-4_4_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc0df4d0e7e3241860fbe8388ba773ecf8455483874af23d7ef4c4e62feedf97 -size 31038 +oid sha256:6ccf57a4907f263a10dcbf9eed8c64ce646e603f8152dbe8e041ae8839798020 +size 31044 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Night-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Night-4_5_null_0,NEXUS_5,1.0,en].png index 4b79f83582..55017d72a8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Night-4_5_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Night-4_5_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e83a879d73aec0356e2a5a2db628183108b2d045a9bbb72d86324419d3407b0f -size 21460 +oid sha256:0ab61e6f09be66482c0afb7f0667ed249b2b1e67bb6b41cfc4618237e9013bef +size 22278 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Night-4_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Night-4_5_null_1,NEXUS_5,1.0,en].png index d1b193a630..832bb11ca5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Night-4_5_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Night-4_5_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a317ad736c41bfa5380e7743f28814a5601dfa87919f7edb35d0a4f09544762f -size 20701 +oid sha256:51b76bc7cb46c756b27ff92014eaee87d15f485817aa770bb59fd5339371ee17 +size 20730 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Night-4_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Night-4_5_null_2,NEXUS_5,1.0,en].png index 506ca9ec10..d5ca4251fb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Night-4_5_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Night-4_5_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5a8f4050dfe8a744191b439609b639bd5cd40b58a5b8af6762812677b64221be -size 26848 +oid sha256:f8a2e0e11c542132e649ca5003af7da4ae346b43c6aef8db9f2dbc2dfdc2d53f +size 26877 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Day-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Day-1_1_null_0,NEXUS_5,1.0,en].png index e5ef44e8d2..10ec2fb2cb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Day-1_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Day-1_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c4863b3c785f8f2ea7561dda464eae2a7bfdab4e08d12bcad1659dbea358e2d -size 37187 +oid sha256:556ac89ad4a4f4a2f204f84634ce63ad8fef43bdb26edba8a78f7bc06d13b374 +size 37179 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Night-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Night-1_2_null_0,NEXUS_5,1.0,en].png index 33e9145d85..2fc8eaee08 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Night-1_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_NotificationsOptInView_null_NotificationsOptInView-Night-1_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb1fcd755cbc62fe56f37bc2b43c5d80285fa7233e238fa1cedda151c21841f9 -size 33720 +oid sha256:636756772f8e4eba11e88a7903f04c545cc93045961c7747ddfa2fcb24533754 +size 33710 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Day-2_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Day-2_2_null,NEXUS_5,1.0,en].png index bcc1a519e6..ff92f02572 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Day-2_2_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Day-2_2_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:261face86126e646561ef480904b0af7e7aaf990e5a6fd198d0d953f4a613e11 -size 288233 +oid sha256:8d56633c5bddf14c18ebab5859b0f2aaee9c37c1ff8a8923b508827880e708b3 +size 288239 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Night-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Night-2_3_null,NEXUS_5,1.0,en].png index d61546f9d8..3440c75136 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Night-2_3_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Night-2_3_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c935cbdc16bead41781b486da0427a2d6df3530fe8f35a8bb031c660db8e7e73 +oid sha256:04b8af7d14aff6edd9db4e4c6187381c9efbdba1721e1688da5823038f1aa31f size 391103 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_2_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_1_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_2_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_2_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_1_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_2_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_2_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_1_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_2_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_2_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_1_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_2_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_2_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_1_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Day-1_2_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_3_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_2_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_3_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_3_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_2_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_3_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_3_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_2_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_3_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_3_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_2_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_3_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_3_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_2_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_InviteSummaryRow_null_InviteSummaryRow-Night-1_3_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_0_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 153fca40a1..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_0_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:90bdbaee0cc44c246e8cb0e1f685facf6d4aa40118f0fafdea0ec25255fe0f8d -size 54700 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_0_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index d022c40230..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_0_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2e934e2defeaf5b246d47680cde2221f7b7dac84f233407d8366f503762d12c2 -size 9023 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_0_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index e274ec5ab3..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_0_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4d1eb6909c600aef67978922cd565af5f55ea4640fc355ac354613e2b304b9c3 -size 53932 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_0_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 31fbbb4768..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_0_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a3dfebc124e08a0ae7ba5d254ed9c078b5d9004e88665d91b0eb80bf145a3a9c -size 54713 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_0_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index 8cfe321257..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_0_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:eddbd29af458a0776699e5108cfe6c685e4f4c8bb12963dfac938d109b799cad -size 44421 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_0_null_5,NEXUS_5,1.0,en].png deleted file mode 100644 index 8cfe321257..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_0_null_5,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:eddbd29af458a0776699e5108cfe6c685e4f4c8bb12963dfac938d109b799cad -size 44421 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_1_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..aaf2b306c9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_1_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2629cd4ebed10253756b80543ec11f34420549c561360734c8528d3f1579500c +size 54698 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_1_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5d62fd19bd --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_1_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:724f8f5923887337cea327fa252347435215916404e41a0844badbb9af8863cd +size 9024 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_1_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..0199a2a041 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_1_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ec43db744d228fffc19f9e39abe65d8804818c3ffc6691ad8ede89459ff9af4 +size 53938 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_1_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2be626e9a4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_1_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:838083b614f34848e21b892f4c8c3efb2f7fcf5ef32b3545b722a29b9ff3bb0d +size 54714 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_1_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ac248edb32 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_1_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c5f4bd13cf3d8810d77ea4db2b2415bb1983a5e0afa0b32e1d7d6a38ad4fcf77 +size 44426 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_1_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ac248edb32 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Day-0_1_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c5f4bd13cf3d8810d77ea4db2b2415bb1983a5e0afa0b32e1d7d6a38ad4fcf77 +size 44426 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_1_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 0e95e8364c..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_1_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:727289c1a0bf3b98c723a5baac8b25ccebd4d0e9e21cc16d0ef52f5d76e8b768 -size 51857 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_1_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 6f7d3fd4d7..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_1_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f78607796efb5ba1be8eb4c7d3072ed6634daf0c9ecf39764c8a849f5d27f8c4 -size 8758 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_1_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 10527ca1e9..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_1_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fab7060aac1536a4206f7553064212ef8ffd33745fd14b54d10aa90cf3827c9c -size 48900 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_1_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 280ae830be..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_1_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4da2eb80232b80e99472a9c890843ff13b8b828b3e6a9d1a5a6c1a0da678a8be -size 49631 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_1_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index 48b6d10405..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_1_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7927e9e5ed238f1d6113ba114826bd9d5bf30c10b7a5830234daa545ae56a3e3 -size 39708 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_1_null_5,NEXUS_5,1.0,en].png deleted file mode 100644 index 48b6d10405..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_1_null_5,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7927e9e5ed238f1d6113ba114826bd9d5bf30c10b7a5830234daa545ae56a3e3 -size 39708 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_2_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..fc98f2a33f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_2_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b34e01d78278ffe928a81b5608b4381998ada1e027769df374fe717c7b7a3ad +size 51857 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_2_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..cfec1bb141 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_2_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:047abaedcf9015ce5f3f0d94890c97a9fa2b873df316e1cd264e3283b2f09871 +size 8762 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_2_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6f6b3f8eba --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_2_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c785666c3bcf596ce4d03295deeb9b3fee15048676808851f995a84730b0be55 +size 48914 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_2_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..05989ba693 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_2_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b15d7adb06533e7d9a96bb6c0ae4de3cb46393c622e0f6832c1885800641ec7b +size 49644 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_2_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6a0cf50b96 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_2_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:504cd58392efa7bea677a84c2aaa92243eedbab367659b5271e135d495d265fe +size 39722 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_2_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6a0cf50b96 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_InviteListView_null_InviteListView-Night-0_2_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:504cd58392efa7bea677a84c2aaa92243eedbab367659b5271e135d495d265fe +size 39722 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_StaticMapPlaceholder_null_StaticMapPlaceholder-Day-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_StaticMapPlaceholder_null_StaticMapPlaceholder-Day-1_2_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_StaticMapPlaceholder_null_StaticMapPlaceholder-Day-1_1_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_StaticMapPlaceholder_null_StaticMapPlaceholder-Day-1_2_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_StaticMapPlaceholder_null_StaticMapPlaceholder-Day-1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_StaticMapPlaceholder_null_StaticMapPlaceholder-Day-1_2_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_StaticMapPlaceholder_null_StaticMapPlaceholder-Day-1_1_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_StaticMapPlaceholder_null_StaticMapPlaceholder-Day-1_2_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_StaticMapPlaceholder_null_StaticMapPlaceholder-Night-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_StaticMapPlaceholder_null_StaticMapPlaceholder-Night-1_3_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_StaticMapPlaceholder_null_StaticMapPlaceholder-Night-1_2_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_StaticMapPlaceholder_null_StaticMapPlaceholder-Night-1_3_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_StaticMapPlaceholder_null_StaticMapPlaceholder-Night-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_StaticMapPlaceholder_null_StaticMapPlaceholder-Night-1_3_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_StaticMapPlaceholder_null_StaticMapPlaceholder-Night-1_2_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_StaticMapPlaceholder_null_StaticMapPlaceholder-Night-1_3_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api_StaticMapView_null_StaticMapView-Day-0_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api_StaticMapView_null_StaticMapView-Day-0_1_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api_StaticMapView_null_StaticMapView-Day-0_0_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api_StaticMapView_null_StaticMapView-Day-0_1_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api_StaticMapView_null_StaticMapView-Night-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api_StaticMapView_null_StaticMapView-Night-0_2_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api_StaticMapView_null_StaticMapView-Night-0_1_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api_StaticMapView_null_StaticMapView-Night-0_2_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_0,NEXUS_5,1.0,en].png index 6eb4f902e9..b464feab17 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4fe166a1cb630b39bf4d21f78da1cca582a466e7cf50a879546db9ccb123cb7e -size 22385 +oid sha256:c5e1c5e15e6dea0a0893d1e8c01f47fa5d00d42b53d5f92b412a1a26f3ca7c45 +size 22384 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_1,NEXUS_5,1.0,en].png index 80f5a454bd..64344e619b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ee8c200c15a0b9d9aefb2de0f3dec576742ac4fe59c71ce0ee016cf23415d5a -size 41422 +oid sha256:293345711016a439679a23d9e9e97dfebed9bf3a86eed1c56c981c29a9a19615 +size 41425 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_2,NEXUS_5,1.0,en].png index cb9b8a183b..c6154da74e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc7691f9c3643ad352bf687a898512b4adb4ea0b12e536ebda3bce7c8f2512d1 -size 39954 +oid sha256:90ba46eb2d6f870201f7f496ba3b1a5b5059bd3e28f5ada6a861aab8d8cb17ff +size 39955 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_3,NEXUS_5,1.0,en].png index 6eb4f902e9..b464feab17 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4fe166a1cb630b39bf4d21f78da1cca582a466e7cf50a879546db9ccb123cb7e -size 22385 +oid sha256:c5e1c5e15e6dea0a0893d1e8c01f47fa5d00d42b53d5f92b412a1a26f3ca7c45 +size 22384 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_4,NEXUS_5,1.0,en].png index f6d0210995..5f2e30ca50 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e083f35c919d664603c4edeb24a32e9a9e594a480d4b4ebabd92863a77d7af3 +oid sha256:6316c40ccd726f58d24bee34238e785d6237a45ae8e4b50d33f9ccbf59ed9a79 size 22504 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_0,NEXUS_5,1.0,en].png index cfd15d6bad..9e922bc07d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b1cd421332c29fe6ea004f95c113fa8bfb0ccc64b34c77b5d03c93debf6fd758 -size 20780 +oid sha256:7188c2ab6aa4ba0ef3f52d296130e455aab65628b1beb153fb1829bd4bb896a2 +size 20778 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_1,NEXUS_5,1.0,en].png index 0a931fcc53..b8c9a40cf8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:38539e5bf70c45e79cea35d6d5cd15ac1476a51083a177d82eb2765673733fc0 -size 38110 +oid sha256:e635b9bcf579607a5e97e2c96e1a1859e32946ee5322a7dc689ceaa1a4033f0b +size 38126 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_2,NEXUS_5,1.0,en].png index 5123e9e4fe..be3049fc3e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d9fb2ef1976c318c4dc7191a10d482e98ceb8651a90cdef933c100cff4bdb6a -size 36560 +oid sha256:02b1239762d6942bd3da1a949793957a117f1b2792f55fe21f347915751ba54f +size 36572 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_3,NEXUS_5,1.0,en].png index cfd15d6bad..9e922bc07d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b1cd421332c29fe6ea004f95c113fa8bfb0ccc64b34c77b5d03c93debf6fd758 -size 20780 +oid sha256:7188c2ab6aa4ba0ef3f52d296130e455aab65628b1beb153fb1829bd4bb896a2 +size 20778 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_4,NEXUS_5,1.0,en].png index eab3525a36..0744b20dde 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0e197302e95daa0a1bb1461e0564afd234723db0300521c623cc3640c56f431b -size 20938 +oid sha256:b55a82515f34ead38768210ff200fbbfc7e092f227e4699aa4e81391e5878b4a +size 20936 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_0,NEXUS_5,1.0,en].png index 390f4e6623..2f722ff261 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5bbf5bf95e68def0f8e6da83c649ab0e511fef68869e3aa6523fcd27c4e774b7 -size 13686 +oid sha256:7d8bdbbd3ca3e9f4306af4b3f2d07acd224b19fad5e42be32f0c934beb886796 +size 13659 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_1,NEXUS_5,1.0,en].png index 2e27aae665..7bd51b263b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:812f724738df351229c4a21acef24e4ca87b743623f6da6fc9a7e4b61daabf05 -size 36138 +oid sha256:e60a1de1f99ae145f95615f48b6233b872a8155a81c69a50d8a5920f6469e548 +size 36119 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_2,NEXUS_5,1.0,en].png index 8ef5afb54f..08c8aa9737 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4aef2ff025f442667c25c75a7479c69196cf8996c568eabcd9966234b378eb1c -size 34614 +oid sha256:1bad200557be68d9e23d029a622c2b0be407a4539b4225c4db082d493e5e11e9 +size 34592 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_3,NEXUS_5,1.0,en].png index 390f4e6623..2f722ff261 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5bbf5bf95e68def0f8e6da83c649ab0e511fef68869e3aa6523fcd27c4e774b7 -size 13686 +oid sha256:7d8bdbbd3ca3e9f4306af4b3f2d07acd224b19fad5e42be32f0c934beb886796 +size 13659 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_4,NEXUS_5,1.0,en].png index 5cb2831efd..cd5ded8f42 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b99d166a325408d309b97796498b4daf4443cb77467e4a17fe3ea642871e6c3b -size 13892 +oid sha256:910c7cb72c2303b71b4c3a399c13459c664e10ab424cde7a2686b76bacde808e +size 13866 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_5,NEXUS_5,1.0,en].png index 4c6d685db5..f44bbc4e8c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a0df7b9a6cde1039dce7cd63d71b28ea1fe7aaec3ee0ae4a420319f0c4b1375 -size 17693 +oid sha256:dbf585208f96a8a2ecb969dee82825276188c9a2cf33331826528886a38f2d64 +size 17665 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_6,NEXUS_5,1.0,en].png index f364a3d650..558e2f908d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c0694186f358a87eb4a76e426a91d63d50e2f4f8b14663e16656ee9aa7d2baf -size 26668 +oid sha256:9a5d9189e14cdcd64d36b8887de838f12176d56f7e0bf3e91d9ece607a60cf63 +size 26645 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_7,NEXUS_5,1.0,en].png index b055e1b64a..70066fb14a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Day-1_1_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:324e3758b4b1ff314f19c362062c365e5ad245ac71bdf7f186de41213df08388 -size 29009 +oid sha256:95a4ced4dad40d976726af34e006357bb521121892d977b78148fb5e57d4e53a +size 28989 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_0,NEXUS_5,1.0,en].png index 8a098474d5..45ab47fc00 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:819b1b4df7ada9574c1ca295aa233723bdc0cf5da806f700807f8b105655fb45 -size 12495 +oid sha256:218d20578824588a5b920832def83e8673c5537f408496327e098f1f16a59d2b +size 12510 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_1,NEXUS_5,1.0,en].png index 63e6b130e1..b39b12cda6 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d983ed71433b3f833879df41a5cc167854ca939cce0b09fa99eb84a384975503 -size 33033 +oid sha256:295aa9bd3c35aa79259764b0385606e46a3c1aa3c096c44e2028fe4ac4cbfae9 +size 33050 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_2,NEXUS_5,1.0,en].png index 876e85417c..c5dab897ef 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:42ca0b856f8793a8750fec69fd2f9d19ead83164ddb89de607b9997ccd302d93 -size 31400 +oid sha256:f0aabe312bd16972c79d504c9104a6931659c2a990d435c7a23b31f2b5dc4127 +size 31417 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_3,NEXUS_5,1.0,en].png index 8a098474d5..45ab47fc00 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:819b1b4df7ada9574c1ca295aa233723bdc0cf5da806f700807f8b105655fb45 -size 12495 +oid sha256:218d20578824588a5b920832def83e8673c5537f408496327e098f1f16a59d2b +size 12510 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_4,NEXUS_5,1.0,en].png index 310fb8ea22..83e3ca9f5b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:53f6b031d5bb3bf1e33f6ec5449ab5aa8c95e2dd6d844cab5df81038dc54fa3a -size 12685 +oid sha256:96f8422de801a6382cf6b8e5a07e93feae4ca598465e43541c4e60569d7a07be +size 12702 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_5,NEXUS_5,1.0,en].png index 7b8002f1d3..cb921789f9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8cfbc773d5b5a5248edc83fe472035474c557ad6c30cb197a8fef15dae1d557 -size 15914 +oid sha256:33ba6a9ea9c5bae04baef6cbcc1f7b458fbc833fc91c747feacf8df2d723f8d7 +size 15928 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_6,NEXUS_5,1.0,en].png index dcefcff71b..4796b96125 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:227d97d78c52e9bbc8aa141666bedb72cc43d79c2af5fce89f2c9002c3b4ccf7 -size 23477 +oid sha256:228268861572b50075416a8404ccf77fa1a7c06601e30cecb831ae2b829ea1c0 +size 23490 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_7,NEXUS_5,1.0,en].png index 197c84d3f6..228dc1a216 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_ShowLocationView_null_ShowLocationView-Night-1_2_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:41b5113a41c1fc0b1ac6228a6344a92e8765fdc6834c900571f0556ff0dfec40 -size 25574 +oid sha256:759a6abdf9ffff8a6deadb46d67996e832fca3308edd651b351ae0b4e96b0114 +size 25583 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.components_PinEntryTextField_null_PinEntryTextField-Day-0_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.components_PinEntryTextField_null_PinEntryTextField-Day-0_1_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.components_PinEntryTextField_null_PinEntryTextField-Day-0_0_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.components_PinEntryTextField_null_PinEntryTextField-Day-0_1_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.components_PinEntryTextField_null_PinEntryTextField-Night-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.components_PinEntryTextField_null_PinEntryTextField-Night-0_2_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.components_PinEntryTextField_null_PinEntryTextField-Night-0_1_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.components_PinEntryTextField_null_PinEntryTextField-Night-0_2_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Day-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Day-1_1_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 978b895828..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Day-1_1_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:55699e4df8454188d18cff9476f52ad73066b740c67b15eafaf4003cb3bee62f -size 18567 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Day-1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Day-1_1_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 678036c4f6..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Day-1_1_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5e36041959b02f320438ae5627559b52538a79e5bf813c5beec2dc30c0ba9e61 -size 20909 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Day-1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Day-1_1_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index bc57c63a27..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Day-1_1_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d6ca257ae106de5d4361464132d97febacc09f211e5697d0da03c767fd8a2d05 -size 32036 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Day-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Day-1_2_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b37ddb7ca6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Day-1_2_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8901be1ea307698f7f0ffe9238223a9865f45c4c9ec6656034f2e4be4a4166c0 +size 18566 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Day-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Day-1_2_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..92223104d6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Day-1_2_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d11927f7a8b40f859d755a983f03bdf0d8086e38362ce00c41c64108c80d0b9 +size 20909 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Day-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Day-1_2_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f96ac52eb1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Day-1_2_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:92e3c6ccd82baed994d524bf30019127b28891798bc495831f83d4224649128f +size 32041 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Night-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Night-1_2_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index dfb8d18f18..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Night-1_2_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:71b5f1d9d01cd1c4ba0fc34c636fefb54099540586097d2c789befb36f69e68e -size 17124 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Night-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Night-1_2_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 4fc8cff00a..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Night-1_2_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b2eb7e4911680f6660823a685e1fa8bf12ed623955685792dcef80ac7b25fb26 -size 19438 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Night-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Night-1_2_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 0f525564e4..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Night-1_2_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d28f9cb600a93d861fcfb866f876eec6cf422258e99dcdd66cbae2338d5406c2 -size 28289 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Night-1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Night-1_3_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b57e28025e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Night-1_3_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50952c3d099c25c8cfb115c002cfe29f7eeb44dcb6188e63bbe226c3a524b9d1 +size 17123 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Night-1_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Night-1_3_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d441d2ede6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Night-1_3_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a85234663bb755beffbfb3a520d8c499e1286a8a017c9d50a37c3a402c496d71 +size 19438 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Night-1_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Night-1_3_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..90b058e9f2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.settings_LockScreenSettingsView_null_LockScreenSettingsView-Night-1_3_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe69ed40eceb02517b602b14cca8ebdc3369f890feef8be38ffeab6b4b75bd00 +size 28302 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.biometric_SetupBiometricView_null_SetupBiometricView-Day-2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.biometric_SetupBiometricView_null_SetupBiometricView-Day-2_3_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.biometric_SetupBiometricView_null_SetupBiometricView-Day-2_2_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.biometric_SetupBiometricView_null_SetupBiometricView-Day-2_3_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.biometric_SetupBiometricView_null_SetupBiometricView-Night-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.biometric_SetupBiometricView_null_SetupBiometricView-Night-2_4_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.biometric_SetupBiometricView_null_SetupBiometricView-Night-2_3_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.biometric_SetupBiometricView_null_SetupBiometricView-Night-2_4_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_3_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index c3d918b1c2..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_3_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c9f1f9f900cea024cdf777867c33c1d1bbe4744a5b91cd994a03aa6cbf4f8815 -size 33186 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_3_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index a27cd2534b..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_3_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ae1ac3fa371e4deb0851baa75a5e68635d8ee45eaea6dd8e373768df8cf237a1 -size 32925 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_3_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 61c45c675c..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_3_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:13f2b0ac39e3e5448916beea6f7a7183c8a205811343d3ff5e8a21a4d2d3f641 -size 32447 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_3_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index f96a3743f3..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_3_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f031423cd98572a729f54e0bc4cdb50778aadd732aa93cf48d063075e9da00be -size 28636 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_3_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index 8a290a2303..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_3_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2818a043ea06af263b1442c6e90727509da2ce2da036d68dce89de31fa8b013f -size 35614 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_4_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8e1b99d93e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_4_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:adcb32e2e8b14a74536035e51826883d4ee40b8c87f65b49cebcec02c1528e26 +size 33187 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_4_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..76a46aecdc --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_4_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:35c212114bd95643bcc6752044f1f99ccb8fa3bdb7a2584bea4d398ef872d244 +size 32926 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_4_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a36f53e408 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_4_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:334e5b9be8e3a5c30a3a1aff084e8400283a53314b9cd9cfe1679d029c93ab34 +size 32453 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_4_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..57f1ed60ea --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_4_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a4b800dd3174b307c78d587d956d3a7d526df5ddc150e3951eb1c932910df76 +size 28637 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_4_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..206fbfd3d6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Day-3_4_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f3121f6ce6741501be10c6d36a46ecbdd7ab79cb3c1278a49cabcf28c4d4e57 +size 35608 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_4_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index c1fb8f128f..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_4_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:75145d74e0bebbad427626711d5bc86911bd89350e73c968c8ca9448e939cdbb -size 31954 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_4_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 8508c0ba62..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_4_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:881e580f855b653e5b4159f18c948aaf5636bebca97d0f687c3bdb32bd805a46 -size 31899 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_4_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 4b0a1988c5..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_4_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d0a34e1c3c047806e4304791664c25cd8d7c9e7850c24b2bb17119e0d83fd5d4 -size 31492 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_4_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 64d049871a..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_4_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f19ab48f8f165527b08e3b05df4b66d007c3d20dcf88daaf2f37793395737712 -size 25175 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_4_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index 4c852519f8..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_4_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6de6787421a27c30735584c6827465498fd3a4f28fe75a1f8cba613d62dd3d43 -size 30905 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_5_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4988bb16f1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_5_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:37256f158b8e2d6b114837f3cc13921bb44bff6cd25d5946b2255b103da3948c +size 31953 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_5_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2bf84075d8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_5_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:38fd545217d797f921bd6c591481f56a2f41f165c5007f65534ce1d129a6d93e +size 31898 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_5_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5cadc44925 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_5_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:678a57608f727771be98000703fa2e0c0d53bcb81cce26ab98120f041fad7d75 +size 31493 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_5_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_5_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..9cd344c9c5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_5_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d9569bd5accded45b38555da99edd65195c352c444e8d25ec05f63a86dcc730c +size 25188 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_5_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_5_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a66a64d318 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.pin_SetupPinView_null_SetupPinView-Night-3_5_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b4e13d3dec625ca8e7069528eff31daa6a38254a98e7f70db0c1521e68bbafa4 +size 30925 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock.keypad_PinKeypad_null_PinKeypad-Day-6_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock.keypad_PinKeypad_null_PinKeypad-Day-6_7_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock.keypad_PinKeypad_null_PinKeypad-Day-6_6_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock.keypad_PinKeypad_null_PinKeypad-Day-6_7_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock.keypad_PinKeypad_null_PinKeypad-Night-6_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock.keypad_PinKeypad_null_PinKeypad-Night-6_8_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock.keypad_PinKeypad_null_PinKeypad-Night-6_7_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock.keypad_PinKeypad_null_PinKeypad-Night-6_8_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_6_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_5_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_6_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_6_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_5_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_6_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_6_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_5_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_6_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_5_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_6_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_5_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_6_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_5_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_6_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_5_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_6_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_5_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_6_null_5,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_5_null_5,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_6_null_5,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_5_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_6_null_6,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_5_null_6,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Day-5_6_null_6,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_7_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_6_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_7_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_6_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_7_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_6_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_7_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_6_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_7_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_6_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_7_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_6_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_7_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_6_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_7_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_6_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_7_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_6_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_7_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_6_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_7_null_5,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_6_null_5,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_7_null_5,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_6_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_7_null_6,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_6_null_6,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockDefaultView_null_PinUnlockDefaultView-Night-5_7_null_6,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_5_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_4_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_5_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_5_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_4_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_5_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_5_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_4_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_5_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_5_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_4_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_5_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_5_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_4_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_5_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_4_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_5_null_5,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_4_null_5,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_5_null_5,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_4_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_5_null_6,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_4_null_6,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Day-4_5_null_6,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_6_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_5_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_6_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_6_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_5_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_6_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_6_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_5_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_6_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_5_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_6_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_5_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_6_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_5_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_6_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_5_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_6_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_5_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_6_null_5,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_5_null_5,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_6_null_5,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_5_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_6_null_6,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_5_null_6,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_PinUnlockInAppView_null_PinUnlockInAppView-Night-4_6_null_6,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.accountprovider_AccountProviderView_null_AccountProviderView-Day-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.accountprovider_AccountProviderView_null_AccountProviderView-Day-0_1_null_4,NEXUS_5,1.0,en].png index e6096ed152..0483e80583 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.accountprovider_AccountProviderView_null_AccountProviderView-Day-0_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.accountprovider_AccountProviderView_null_AccountProviderView-Day-0_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f368e6b153eab423ca5d4eac3c5325293b4a34b21f428de7f2c4a1712900faaa -size 7062 +oid sha256:0a51b273d35c500cf83dbea0a60b166a1523fb3a78c4da2cb6dd13d97d466423 +size 7059 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.accountprovider_AccountProviderView_null_AccountProviderView-Night-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.accountprovider_AccountProviderView_null_AccountProviderView-Night-0_2_null_4,NEXUS_5,1.0,en].png index 3cd1c0467c..d30f5e6ab8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.accountprovider_AccountProviderView_null_AccountProviderView-Night-0_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.accountprovider_AccountProviderView_null_AccountProviderView-Night-0_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8ef1dc02b468d3aa21bc0a4452edb007799f0038402be6f8ea24c5d4e3ef4bd -size 7047 +oid sha256:965c9f880aac0dd0a60f288e0667fb28eda218657ee6eaff9440aa4da9fcb784 +size 7050 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_null_ChangeAccountProviderView-Day-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_null_ChangeAccountProviderView-Day-4_5_null_0,NEXUS_5,1.0,en].png index ff7779939f..469c6abd55 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_null_ChangeAccountProviderView-Day-4_5_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_null_ChangeAccountProviderView-Day-4_5_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d41ebbf26c419c5ffe5f2e37b31a8297fbd3aef38fcc9e7b2d2cbfc91691954 -size 50801 +oid sha256:4d4fc4de7eb980f7ea54aa415ac6cbe6e95c8cd263b968bb88ab19d073a78c3e +size 50803 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_null_ChangeAccountProviderView-Night-4_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_null_ChangeAccountProviderView-Night-4_6_null_0,NEXUS_5,1.0,en].png index bd253cbe8c..77646fbc02 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_null_ChangeAccountProviderView-Night-4_6_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_null_ChangeAccountProviderView-Night-4_6_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f5e96ca97984f3d5a5c8def6222d9d3c88bbaa7499e60c56a1d1813939a9133 +oid sha256:e75c5db7e371067a8d8a870ee61718bf0f17397aa2e5e61efd521346202ceba2 size 49317 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_0,NEXUS_5,1.0,en].png index dd0e30059c..3bdb7f4d9c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a5ca3bd244941e910ae25e5d2d68f3aacb4ad7d89ed6439168c931d3b1f11df -size 39644 +oid sha256:9cc2c0ee0df61c3b071185daeb2e20d64f129db2b3e9b5f2d9bb68dd2b04ca71 +size 39655 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_1,NEXUS_5,1.0,en].png index 63cffc0ff9..54fb4fa2dd 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5172b5fd76582589f6dec777428dc73aaeb42d94151da08afc32a629554309c0 -size 40804 +oid sha256:27b8b17d2f628e9bfa5f0d97bf2cf68424261596967d1b2621a5bb879f2ce2d1 +size 40815 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_2,NEXUS_5,1.0,en].png index 685f99c2d9..f198ed2c07 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Day-6_7_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:544c75b9711b7baa0f63d1219c33281a143b3cb0ae601edcb2c070e7c4c57a0c -size 30747 +oid sha256:ec89cd219a741b98ae046b2c90db9d36d418570f05d0edf41cfe831cf71b1303 +size 30745 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_0,NEXUS_5,1.0,en].png index 0168b4f9a8..179219d286 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c21cb3f0b3b594a8b3b63762ea7c0864ed6010d3b8479be35e87ffa215bb9a43 -size 37570 +oid sha256:0a240b641665c15d79663a4f35fa7af923bebf88eeac6355c9c1ff127794ba77 +size 37585 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_1,NEXUS_5,1.0,en].png index c5b72f4517..44a81090b6 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b80d77309b1fee94e8eb896973a637a3ceabc97de6b06e2d177815b0aae47829 -size 38567 +oid sha256:7e32c7c81e3def45b6df8aa8f1da70012f656a2289f0db562fd5df57c00ca104 +size 38574 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_2,NEXUS_5,1.0,en].png index 85b38120f9..dfadb50fef 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_LoginPasswordView_null_LoginPasswordView-Night-6_8_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8fc290ee3547444b85fa14825b85571b1aa8293d5e951c9e62c89a880a9c85d -size 27393 +oid sha256:b8a15fc7d605ed1c4ba2bbfa901ca01b44ce0fb5c678be6b5fb484b721f95c87 +size 27410 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_SearchAccountProviderView_null_SearchAccountProviderView-Day-7_8_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_SearchAccountProviderView_null_SearchAccountProviderView-Day-7_8_null_0,NEXUS_5,1.0,en].png index 47179975a9..e2847efcd2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_SearchAccountProviderView_null_SearchAccountProviderView-Day-7_8_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_SearchAccountProviderView_null_SearchAccountProviderView-Day-7_8_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:033d639153d83e7b1e7c88195d999243ed7131822f25a4c74904334cd37e7329 -size 25947 +oid sha256:a47d05a9b46d28cda3716f198899054d24cc53f5f4ff30fdfd7b58c520614bf4 +size 25931 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_SearchAccountProviderView_null_SearchAccountProviderView-Day-7_8_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_SearchAccountProviderView_null_SearchAccountProviderView-Day-7_8_null_1,NEXUS_5,1.0,en].png index 2612d19e10..0d97ab2655 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_SearchAccountProviderView_null_SearchAccountProviderView-Day-7_8_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_SearchAccountProviderView_null_SearchAccountProviderView-Day-7_8_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7573d66c1663829cbed5d793acf726c8a851976beaf2095399b57a67d074f894 -size 55798 +oid sha256:235ba7fffdcc496df81b32ab3080214de438797989219d18ad9d1de5c5da3a48 +size 55790 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_SearchAccountProviderView_null_SearchAccountProviderView-Night-7_9_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_SearchAccountProviderView_null_SearchAccountProviderView-Night-7_9_null_0,NEXUS_5,1.0,en].png index 9c07d61c37..0068e7b3ad 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_SearchAccountProviderView_null_SearchAccountProviderView-Night-7_9_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_SearchAccountProviderView_null_SearchAccountProviderView-Night-7_9_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dfb124e9735a84d623524d82d93abc2316d878825007f8e84aa714c9bf0b1ed0 -size 25055 +oid sha256:cb45bc8b8352f8c26509eae5f153fcbc528dbb9cc4b98e1ce3a1cee107075634 +size 25047 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_SearchAccountProviderView_null_SearchAccountProviderView-Night-7_9_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_SearchAccountProviderView_null_SearchAccountProviderView-Night-7_9_null_1,NEXUS_5,1.0,en].png index 5a10858510..9ed407dbd4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_SearchAccountProviderView_null_SearchAccountProviderView-Night-7_9_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_SearchAccountProviderView_null_SearchAccountProviderView-Night-7_9_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd41270c0b7bf19a42398d6939551ac975e31f456c96c78805583032f05c209f -size 53638 +oid sha256:c5fd664e126c30463e67fd8ddcb46d44d49983c1b23a0f20eddec13a20f99f62 +size 53628 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_0,NEXUS_5,1.0,en].png index a876131abe..c930a5e486 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:75e44003e66f73983d63b0735217a3f99d1ef2af5778d53e256ab3c644c59cb8 -size 29114 +oid sha256:78edb2f0cbb3a7c975999aa31f90e5a82e7b73a21f2923e420f364da3c0118fd +size 13706 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_1,NEXUS_5,1.0,en].png index 30b0828aa0..4bd4efb227 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a9b3f53a32a0924ae1db60f01df80e1e098d332ae29fdbfda527fb69b6a8860 -size 33097 +oid sha256:186d3f45a3e8b294e523ede9787563399d97b26d7181ad687afcd8b48f8be506 +size 41247 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_2,NEXUS_5,1.0,en].png index 41661a8a3c..99ff03c0d8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1807fd9e347c29c853d925c28f1e00259865eb9e7990fca1ad63e90a9eea8a1d -size 30676 +oid sha256:6c6252e151e747259945691f9461e4e4d893abd6f048be336df5fb75ff558be7 +size 30679 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_3,NEXUS_5,1.0,en].png index 01bbb30c88..4bd4efb227 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c140c29ad4a849893e08ed5b1943346ebc7ae5b888fb98b97088d2a5f025087 -size 37967 +oid sha256:186d3f45a3e8b294e523ede9787563399d97b26d7181ad687afcd8b48f8be506 +size 41247 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_4,NEXUS_5,1.0,en].png index c627fa8103..4e3d0eab1c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d329bff55f22efa99e8afa20775a25f4c7e717cf5797ecc7a2c0f47e4cc1f69a -size 34937 +oid sha256:2d8dfacb3767fe7e9de7996cd0fb9bcb7f208e49498bfdc7b599546669b7b396 +size 27632 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_5,NEXUS_5,1.0,en].png index 0e2172b118..e9f9d8d5db 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b72c4ecdb641d2adf53c092ac1ccc85018a747ffb0f4b911d3b10db6524ea897 -size 31588 +oid sha256:51528fe646b2ac7f679861af28443b22fe1a710586ac70aa730d98cf7c1e8856 +size 21842 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_6,NEXUS_5,1.0,en].png index 9289d1049b..72f4c9d66e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:704aac22e5063a3cc9b831c9079f1348385a1251c91630575200c33528fe3d76 -size 33480 +oid sha256:47f4ac4fb71f8a4adc3890101244c4ee9fbbf87d0da0e0381a85792b79896a5f +size 26372 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_7,NEXUS_5,1.0,en].png index 8c046ef478..4e8080e3cf 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5527d88e8a960a4c644f1a92c59c3f7c65b13decefcbd6b2679db3d64facf010 -size 39111 +oid sha256:394d3da2d14c0c9438632d54c0999a6ecae9b2ca8cf8bb77986d761d2dfa0a1d +size 39116 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b23d4ec921 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:87010d4aae66e1030d4b9557e4f065df777324495459c08513040e6793d2d00d +size 36675 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_9,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..00eb708f14 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Day-0_1_null_9,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c2f057c86716407a4cd3491612f57b9d4b800cf4ef749c2e6dce3e12fbbf632 +size 37972 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_0,NEXUS_5,1.0,en].png index 987b2930e6..5915ad7d40 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac0f94f62f6d89475fde477b095dc1242a606d9cd4bfa45a8cfaa8e368e22707 -size 27385 +oid sha256:dd5acfa31ca9994634286aa59bdb67d86e438165296164db82e152cd56868585 +size 13044 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_1,NEXUS_5,1.0,en].png index c99598bb6a..b28650d35e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9f843cb5d7bceebbc6508257f0410af14242b23407ecea4643ab8a75329acca2 -size 30994 +oid sha256:49df4ac1a7ec108308b82d0fdb6f0e0d8ca2063960070fa9865562e59774ec21 +size 38623 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_2,NEXUS_5,1.0,en].png index 7e0058627c..9fc78733b4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d9690fdef2aaabadb890c8649195de83cd60306f109346d292951ff6694037bd -size 28955 +oid sha256:d8406ed0ada582c9436be5937d8cb010f3b4692962e5357c0a919471574aabb3 +size 28958 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_3,NEXUS_5,1.0,en].png index b4f15d6aa3..b28650d35e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c75561844b2e5d163c7cdb49a36eb4f3adb4119fda2b9e4b6d9552daf447b907 -size 35817 +oid sha256:49df4ac1a7ec108308b82d0fdb6f0e0d8ca2063960070fa9865562e59774ec21 +size 38623 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_4,NEXUS_5,1.0,en].png index 3bb60b709d..1906882931 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:996388f47d4cd4c7c6318d335204a1b3610272ea1b340e4e36f10da222f3bcb6 -size 30476 +oid sha256:51af5a971e3bb5958646ec1866ba41fd4d46e281713573b005bc71b4762ec7b4 +size 24085 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_5,NEXUS_5,1.0,en].png index 21c4bbbf0d..2c7916bd2b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa9f65a1f2e08e65f21aae2b4b6fb17b980262196443aec6b9cbc6c912077c78 -size 28497 +oid sha256:6efe41b16360b4cfc891a5ffc99ff147ea5c5324341d5f8b8a2f574d360b5aad +size 19117 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_6,NEXUS_5,1.0,en].png index f4e835079f..8de5498d31 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9f63c93ba36566926aa471375f0bb341e1c989e5b1be9090f771597f91a7a226 -size 29258 +oid sha256:6a2a05a5d4f528302ca1a6f8bd799418f97d511bb808dadbfe806077ab2a9bab +size 22756 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_7,NEXUS_5,1.0,en].png index fddd31097a..968ebc3569 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11ac5615f7abcae9c374b61a0c7d4ecf617ab101285e02eac00d57e200259111 -size 37104 +oid sha256:b923a90e9e8f4c16aac3afdda71b60c52db6ab6a08d046be49c9c7a1e91277c0 +size 37107 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b61e420d85 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3961ac676e29ac3d6f8dede3aed1c0c22d400e6a95ca8c4333691102f1616c42 +size 34922 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_9,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..3a2db9c9ab --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_LogoutView_null_LogoutView-Night-0_2_null_9,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:52bf6e6334dd36e2e39f6957a60c7d54839caffdf47a32a0bb463124fc2c27d4 +size 35820 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_10,NEXUS_5,1.0,en].png index cd2c26834e..5fa5ee3c8c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1aecc7465d731f46d32ea9222022b6f640a3aa86c81a77c93487d5f35bfcad1b -size 28689 +oid sha256:d76345d2c5c7624b4cb0b84aeef2852ccc389e1061400dc7baa40ec551dcc8ef +size 28682 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_2,NEXUS_5,1.0,en].png index 8e3f139608..6efa529a6c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:28300c9143f5701e46ed3b8d77e93d34584f82949a335003bab685ffb4cd1434 -size 38850 +oid sha256:36c89397ca544516ff9eb95fa48edc1c3e5762547dd679a34fadb4fefa79692b +size 38853 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_3,NEXUS_5,1.0,en].png index ae317f2718..130e9efa06 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f79e915bd6c47b424fd34dfa38130a083b00288e0f6eb15c685d78bb9ce29e0b -size 45508 +oid sha256:ada50b20e21f66b48130d46b1721aea92d93d906539f0572879e2829b85e6337 +size 39409 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_4,NEXUS_5,1.0,en].png index d607db48b1..3a4a95ab41 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f332e7d12d5f64a1704d17e5adc42e9eb0ea9ed21a2d116955e2e0e276153182 -size 46090 +oid sha256:1ac680d47ade7e34f736eee63acf5fd88dbbcdae6cc941047bad6c8a3edad274 +size 39951 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_5,NEXUS_5,1.0,en].png index 9f763b3134..53154bab20 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ecc28b4311d27eaaa1c32128ed3c1f3ca03c5bb0c1390b8685c82caed2d6bc3b -size 39662 +oid sha256:3678f3d5c09879979624df3528152911bf1fec3e0b509e3469243626cc904749 +size 39543 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_6,NEXUS_5,1.0,en].png index 8907e18435..911a890692 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b269d050154e28ebf9b1eaa51856e1199490ea7aecc6439fc567bd3b8cbb7d8 -size 39224 +oid sha256:c693e0c433c59095d4941a1210faf06ed66dcda9c07663ed34da94f09ce98db5 +size 40194 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_7,NEXUS_5,1.0,en].png index ecbc7a1bad..1493ae2164 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19f262d8c0786fe8e1781c5934e7ff86f89d36decb2d0566ca35772962b3866c -size 40306 +oid sha256:38f50ce93b24ccfbcc1e811305233503be33dcd3456990dc8f5d293ee985bae2 +size 40886 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_8,NEXUS_5,1.0,en].png index be972081fb..8f6bb12b94 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c3ca04db3a4e6096aa75e547fd059a199aca4b91a31b36e3ae84254a08fbfbf1 -size 41043 +oid sha256:a7c09e06910187c2a920d4b0a75b50ccb4323935fccb6f284be7be2b5cf938b8 +size 40751 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_9,NEXUS_5,1.0,en].png index 718abd867e..21690006ce 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d847380eed52b6842f2926143e189208f11baf8ce906acae23dcf797384cd1c1 -size 28015 +oid sha256:70733bd9d950690e16dd1666e912be62b0845baa102fe0dec9be2e9ba10aeddf +size 27740 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_10,NEXUS_5,1.0,en].png index 6dd671d42a..d546f9c545 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eabb7309eef1e5bf0d496e053c1531f44262e109b3847e2de5e60ffd6df4e942 -size 27505 +oid sha256:47f4450cd559bb9e8541e2d9bb155eb81de1d0c379d764cf3d6914bbcd978248 +size 27477 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_2,NEXUS_5,1.0,en].png index 90f1fbbf34..ae98ea36c3 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9740a5e50f62b47a4daccccfb795b6844033b6578227b93584b3e29a949c0448 -size 37510 +oid sha256:3179f908cca32f43101685567a57599f804aee1abd0a8592cc9228d0feed3d2f +size 37508 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_3,NEXUS_5,1.0,en].png index 78a31c272b..0f07411f4f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6460478383d3dfd4043b129743c508ca6cc7bc64e0635dee51d1de7ba073d3c7 -size 43672 +oid sha256:a7273ab9663ce248470951833fb83771f2b96de224e7f57af4d0e8cd29b067f2 +size 37996 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_4,NEXUS_5,1.0,en].png index ff0850af1e..73ff959e5a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b561a070230bb3fb50a189a969cda1fa723fb519915c8af0b21ec516a71cc630 -size 44220 +oid sha256:5c95cad226307c778bae657cecf1d38b8ebf35a2b46a109586ba6d2c08b04eed +size 38503 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_5,NEXUS_5,1.0,en].png index 6476423b27..35b4e1cd04 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88c8e6bcecef8c8da37603cea72dfdeab1d975d674ff049d8994ab12e9799451 -size 37939 +oid sha256:5f5ef2b3ffb033d19692b881ce252e838b7bad4c5e0f80318ced7ce981a171a1 +size 38120 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_6,NEXUS_5,1.0,en].png index 4ee31f59c8..6dd5633b4f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a639070cb7293c750b153489d1b88a946db8231f818063da86ea38bce282863 -size 37693 +oid sha256:386dff22fca85985560c0529d129c7f4ec9b6890ab482e391a42536ad7cb31fc +size 38764 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_7,NEXUS_5,1.0,en].png index 85a62da13d..5d77a66415 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1a18f1544bd6b2f9592acf3e0347792136116b0c5159591cc6bc4a6071638a3 -size 38557 +oid sha256:4deb1a814c819b425b4c6905e22eae41652748b6bfd467fba0be2c2e51ddcbed +size 39427 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_8,NEXUS_5,1.0,en].png index 91639aed2f..b63e074165 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c18f92cda3af1e3a5524fd6191b45810e20eb3449335fa357e390465e051be4 -size 39309 +oid sha256:f76baf840ed31cb14e4c6cc64282d0e07def31ef1bd217ef2ac9954778eeba16 +size 39306 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_9,NEXUS_5,1.0,en].png index 5d33fdbe24..57e27116fe 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04c5ab1c0d9abebd7e189beb5cebecd5dfaa27a90a24ff499ad72a2ab4d003be -size 26307 +oid sha256:6287636b1370e78cdbbbcbbe46b53815013bb8edd1f5f3fa1fca9537d41f3647 +size 26401 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_0,NEXUS_5,1.0,en].png index 3044d7352c..665c8811ac 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db1af097c46be0b83ff50e61807042319da0abeeb18ac2ecc308421fb7346193 -size 14964 +oid sha256:bb0d3bfcfd75cbd75fd9270ff1dc27090e5dbac79ca8db8a46d91a4c12bc966b +size 4457 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_1,NEXUS_5,1.0,en].png index 33e091b922..4e44ed5288 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:efde1c7f011e0a2021e7b94daa693e34ea13255d9ad5030bdff6b87edccc0758 -size 12614 +oid sha256:cf70dc030f96b929ba3817694a624de715b82719117fc75720c4e3ff8a8188c8 +size 9962 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_2,NEXUS_5,1.0,en].png index a2eb035fbc..665c8811ac 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:524f2068f2ba08f5f3c9532e7b41ea09066b28c799ab95ee4dc21e722a4a5647 -size 28377 +oid sha256:bb0d3bfcfd75cbd75fd9270ff1dc27090e5dbac79ca8db8a46d91a4c12bc966b +size 4457 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_3,NEXUS_5,1.0,en].png index 7c59953d03..850b46049a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ef92d0b6b2000763f5efda39d40e81871f1b35f7d164b30ca267055d7dd8117 -size 26263 +oid sha256:a8e4490d507c06fae92e6f05aed7c31ae5a20e2a52bc23efc7db7d6e1290dc36 +size 10807 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index 8a0c607aef..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fdfe2087c1b0697baa2a4d1ed2dff1114066938071a3a7e3164a1cfd092b559c -size 31066 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_5,NEXUS_5,1.0,en].png deleted file mode 100644 index 664177707e..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_5,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:817e008ff8a7f3619ebf09ccb673c654f1bc1ab8119f8ede919e91020ad228b2 -size 28204 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_6,NEXUS_5,1.0,en].png deleted file mode 100644 index 665c8811ac..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_6,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bb0d3bfcfd75cbd75fd9270ff1dc27090e5dbac79ca8db8a46d91a4c12bc966b -size 4457 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_7,NEXUS_5,1.0,en].png deleted file mode 100644 index d160cfe6b5..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Day-2_2_null_7,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3fe3542419140ac2bd473b44d369a72b140bb2d7e47323321bc340e98ac78b6f -size 24865 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_0,NEXUS_5,1.0,en].png index 4232b929e6..fae8a6fca3 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30c6b224c35ed1bd4d6191b6dcb9fa95b6ffbbee5f0ce617124d54bcf2e1f4cc -size 13848 +oid sha256:8c89ac73df77c2bccb0c2aa80cee1420f78e7d07f0eda89a90bffef55e8cf753 +size 4464 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_1,NEXUS_5,1.0,en].png index b6cd97e0b6..825f2f46d5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:18f82098a4c81d7648ee03f02d5fb15f061553b8e35b315ec260090a799086e0 -size 11759 +oid sha256:a8b6b2f2e2c03e7927ccb93d4fa8e914f7a3a19f4526cda8237e324dda3d9d3f +size 7548 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_2,NEXUS_5,1.0,en].png index db250e3e9f..fae8a6fca3 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2391e9bb20c9e5cb5a25fe1ccc3267bbe4e807ab282681c4e9fb5ca4b7b4497 -size 27106 +oid sha256:8c89ac73df77c2bccb0c2aa80cee1420f78e7d07f0eda89a90bffef55e8cf753 +size 4464 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_3,NEXUS_5,1.0,en].png index 311f6ee899..a4bbfff73f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:609cb79eaabd440908359ae01d0e508aebe55e956fc27f460f4f30d8f9105e2d -size 25456 +oid sha256:a90e9ecf5cc6c042d9a72401b378ae0de46244803a9729fe339df7b7b676e603 +size 8225 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index d97646351e..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5289e07b1e9177cd143c7ad4c28698e398539b41c15d48b295c8080578666740 -size 29502 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_5,NEXUS_5,1.0,en].png deleted file mode 100644 index be12429385..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_5,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1f0d4d45f42ccaa4188910b03c85de2b79655a86a5e6c9ca32cb4516ad8e0557 -size 25723 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_6,NEXUS_5,1.0,en].png deleted file mode 100644 index fae8a6fca3..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_6,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8c89ac73df77c2bccb0c2aa80cee1420f78e7d07f0eda89a90bffef55e8cf753 -size 4464 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_7,NEXUS_5,1.0,en].png deleted file mode 100644 index b5cbe9f875..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_ForwardMessagesView_null_ForwardMessagesView-Night-2_3_null_7,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d1fee1e792bd0fdd7039d418f6f014e895e867de48126c1816fb035cab29981d -size 21676 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 91d87cca80..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0596b9207380d08c11dd15fca3ffdbc586c4efeb02da4a0c452b9034a4f2635e -size 395473 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 55a075e88b..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c3cb399c19390d6efdff9d6df5065bb8fec0f359de8fd4b596dedc2f52be5441 -size 395476 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index f2563cb7f7..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:16f345de6e480549895a0f77b7e066bcebbde69c501f1c58e3e82090e4dc0d87 -size 114646 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index d62f53418c..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:469ae4d0a704ba90dc79ff8db073a87de6fdb5cfc4ebdb71b92595cf26fe0688 -size 395667 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_5,NEXUS_5,1.0,en].png deleted file mode 100644 index e4e9b605db..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_5,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:766c51df0e0ca0c5983060505503ff95d9576cd547ae471d208d7d9c31ead7cb -size 6631 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_6,NEXUS_5,1.0,en].png deleted file mode 100644 index a322a9a364..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_6,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bd2a831abc63de6366f2a4fbac653d551e29fbbbb698f5dc3ae51de04dbf6138 -size 15941 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_7,NEXUS_5,1.0,en].png deleted file mode 100644 index 01188c15b3..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_7,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6b7dea2e1df15375ae07db4e6cf7b2a14a26c0ff1f2cbb11efdaea263d954550 -size 16067 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_8,NEXUS_5,1.0,en].png deleted file mode 100644 index 7eb0638b43..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_8,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:45de22760cb98ede41d039060051e9cee9ceddbb05e300c8db2a72fc5fd6022c -size 14441 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_9,NEXUS_5,1.0,en].png deleted file mode 100644 index 7d3cd078d9..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_9,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:02ef108f718e1ef284f5c20c1bbf29b679848575ba3d189830b2ff57a3f938ff -size 14523 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_AttachmentSourcePickerMenu_null_AttachmentSourcePickerMenu-Day-4_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_AttachmentSourcePickerMenu_null_AttachmentSourcePickerMenu-Day-4_4_null,NEXUS_5,1.0,en].png index d743ef4fd4..b5ee901562 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_AttachmentSourcePickerMenu_null_AttachmentSourcePickerMenu-Day-4_4_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_AttachmentSourcePickerMenu_null_AttachmentSourcePickerMenu-Day-4_4_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d3bd52edfa46104861e6362cdd608988d5ad1f3efba1e4318c0df4a5a129652 -size 26042 +oid sha256:fbe312ae1501ae54df5dbb28aa6a7212f33da5122daf74e3f54d610c29a47cd2 +size 26054 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_AttachmentSourcePickerMenu_null_AttachmentSourcePickerMenu-Night-4_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_AttachmentSourcePickerMenu_null_AttachmentSourcePickerMenu-Night-4_5_null,NEXUS_5,1.0,en].png index 331f2958d9..99bf3f71da 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_AttachmentSourcePickerMenu_null_AttachmentSourcePickerMenu-Night-4_5_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_AttachmentSourcePickerMenu_null_AttachmentSourcePickerMenu-Night-4_5_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a06d8d83e44809aa1f205a425de0f92dcd556ddcaea0904074cb05cd7ab87fd -size 23750 +oid sha256:212852c235076628af8e8314c507be262de97e91a75c19beb69a8f613b2dd041 +size 23754 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerViewVoice_null_MessageComposerViewVoice-Day-6_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerViewVoice_null_MessageComposerViewVoice-Day-6_6_null_0,NEXUS_5,1.0,en].png index 521717b05b..5473a4786c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerViewVoice_null_MessageComposerViewVoice-Day-6_6_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerViewVoice_null_MessageComposerViewVoice-Day-6_6_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5cd4e624c0ed8abe5d22d60992f64f81e91fa41ea7d8772acc5e3832eadbcbb7 -size 10581 +oid sha256:4be4de3884637107d73838a0a3c07525dd9b8903875a9b582c2eb04a0aae320c +size 10578 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerViewVoice_null_MessageComposerViewVoice-Night-6_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerViewVoice_null_MessageComposerViewVoice-Night-6_7_null_0,NEXUS_5,1.0,en].png index 3dd0888ff4..058ca3bd55 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerViewVoice_null_MessageComposerViewVoice-Night-6_7_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerViewVoice_null_MessageComposerViewVoice-Night-6_7_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:01eda47acbf273bf61e60dbbc466aac5e9a4eac7690af4cde2e06e7e1f190bcf -size 10152 +oid sha256:588c14b392dcc8a09646fba6124fe4ef735266501199681ee47688ec73d3441f +size 10150 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerView_null_MessageComposerView-Day-5_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerView_null_MessageComposerView-Day-5_5_null_0,NEXUS_5,1.0,en].png index eb1900be3a..8bd92520f9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerView_null_MessageComposerView-Day-5_5_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerView_null_MessageComposerView-Day-5_5_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b8e9c99cb13a27ebc80c664d185cd3e3ad7b2bf6a7a944022c662ba1ee59ab7 -size 18876 +oid sha256:c21909861ac05d2efd9d7d20995d9924dd9eba1f16bf8231f476d8c734abb706 +size 18874 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerView_null_MessageComposerView-Night-5_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerView_null_MessageComposerView-Night-5_6_null_0,NEXUS_5,1.0,en].png index d1e45b4cb1..15ac8d05e0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerView_null_MessageComposerView-Night-5_6_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerView_null_MessageComposerView-Night-5_6_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6e26cc86e44601b64ed04e62e2e714252896d669c98d5aebb0b0df1d852b8ab6 -size 17261 +oid sha256:c5c4867b13a6c32b93a5656438c8fd5571d90b44808a461946f8c14aa6445bdf +size 17265 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_0,NEXUS_5,1.0,en].png index 8e8d4282e9..9997387f2a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3168e64fb861ddbd160ee09a75bcce7c6f7a1b6d47f73f83915f1af0e152256e -size 45863 +oid sha256:31dae2b78085042c8251425557c452094dc3504dcca1dcd01dcd2694327903f9 +size 45869 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_1,NEXUS_5,1.0,en].png index 283008f0fd..1040fa0c05 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de6b85a67d9149cc8e82fb293ec9b81e963588832bc0489b412dfc7a2bb409c2 -size 47387 +oid sha256:d11dd7cd9a8379e353083163cbe055b4b5b6c9454c85a5b38a582e12548851e2 +size 47389 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_2,NEXUS_5,1.0,en].png index a806573632..c9e03a46ae 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b09b37b87bc25d3e5e46cbafbe468cab3d025fc855fc531fc30a08f4b872520a -size 46911 +oid sha256:7b2ed086ebdccf02fd7a351a86d6b3d24d9d873c5e1cf45fd517d385408e0a4f +size 46914 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_3,NEXUS_5,1.0,en].png index 56fdf44aeb..0641a5ba7b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dbed31f6579154ff6674b7201d6f36b2ea0ad10e20a4b59b59c3cc76f95b78bb -size 45370 +oid sha256:81a4e2043e38142b4702bb48a5e3afb45217320f40fae5328b272bf6f45c2c1c +size 45373 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_4,NEXUS_5,1.0,en].png index b414b0ea96..dfd9fdac46 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4d5b41b5ea9986d7dfab84646ccd89f66f8a36ea3ae870d2773a27e22f51e38 -size 38218 +oid sha256:f77573a16f096d4df39dea1e5121156a7edf0128311b9565d4e33fef5ccc0da1 +size 38222 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_5,NEXUS_5,1.0,en].png index a806573632..c9e03a46ae 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Day-7_7_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b09b37b87bc25d3e5e46cbafbe468cab3d025fc855fc531fc30a08f4b872520a -size 46911 +oid sha256:7b2ed086ebdccf02fd7a351a86d6b3d24d9d873c5e1cf45fd517d385408e0a4f +size 46914 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_0,NEXUS_5,1.0,en].png index b83003bf73..370df762b1 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e5c1354c569a124786bae728a13bcaf878c3a9bafd70074067e51a06f5b9ac18 +oid sha256:0a216880d252aa0114748539016af913fc341e870f1d2b3257ebdbad27fb042d size 44094 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_1,NEXUS_5,1.0,en].png index a9c91042af..94c9c38f9a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ad43f99ac09531dfa53c56317e83fc256b5632c91ac455ca27b7dc62196a9f71 +oid sha256:9a58f924762132384dd86819d807d20c90e424b7e73be34ca1183cd5b82d45f2 size 44850 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_2,NEXUS_5,1.0,en].png index ae79ef4871..235dcf1efb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:692b4493554c2a11b278b8c0421ce3463e883ee9944209bd0f8fc4dc4d325446 -size 44443 +oid sha256:01c0efd6551e7757c30e225ceb6ef02415f786ac798fb407d2702795e11caeec +size 44442 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_3,NEXUS_5,1.0,en].png index 74ad437756..38357ea50b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7be3c56e9f87e376dc381752109554425febc03d49b4294a84a2d50fee35335 -size 43036 +oid sha256:6913d74fb4491c652725fe2615d48b8ea94e421de4930b16a7506c4554a1bb7f +size 43035 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_4,NEXUS_5,1.0,en].png index b1d670fde5..09f9c2d4fe 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cfe5805325dcd18b51fa123ee7961333a273b88d6f2a38c906bba8c70248c5eb -size 34396 +oid sha256:b2dfd2ddf22b72de59747c207587cdee74cdcad61429f6ac6ebc7b3279adf72d +size 34415 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_5,NEXUS_5,1.0,en].png index ae79ef4871..235dcf1efb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_ReportMessageView_null_ReportMessageView-Night-7_8_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:692b4493554c2a11b278b8c0421ce3463e883ee9944209bd0f8fc4dc4d325446 -size 44443 +oid sha256:01c0efd6551e7757c30e225ceb6ef02415f786ac798fb407d2702795e11caeec +size 44442 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiItem_null_EmojiItem-Day-26_26_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiItem_null_EmojiItem-Day-26_26_null,NEXUS_5,1.0,en].png deleted file mode 100644 index a1f01a3ac5..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiItem_null_EmojiItem-Day-26_26_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a97571d655893966be7749c851787ec8173b871a912f8e74f6932e54d524e452 -size 9263 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiItem_null_EmojiItem-Day-30_30_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiItem_null_EmojiItem-Day-30_30_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..1431252351 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiItem_null_EmojiItem-Day-30_30_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3891edef642d713ee7ee3f7f8fef57c07b721e35e3b3ad48362bb40a4742f7d3 +size 9276 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiItem_null_EmojiItem-Night-26_27_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiItem_null_EmojiItem-Night-26_27_null,NEXUS_5,1.0,en].png deleted file mode 100644 index d5254b7ce9..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiItem_null_EmojiItem-Night-26_27_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:99b040639102d5ed77fe2b5dfcf550395668348d711ec8da45e95584b8be9778 -size 9117 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiItem_null_EmojiItem-Night-30_31_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiItem_null_EmojiItem-Night-30_31_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..26b75e7f50 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiItem_null_EmojiItem-Night-30_31_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff10599bdfffe0857ebecff94a67ff5ca39f84f2fe61e01d27d6168b9a4c8661 +size 9143 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiPicker_null_EmojiPicker-Day-27_27_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiPicker_null_EmojiPicker-Day-27_27_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 65a27f228d..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiPicker_null_EmojiPicker-Day-27_27_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1fb074cd58034c90010dc437fc01d6ea77a2a22aa07bc1d4cf0c1730b543b41a -size 186707 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiPicker_null_EmojiPicker-Day-31_31_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiPicker_null_EmojiPicker-Day-31_31_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..084ae61979 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiPicker_null_EmojiPicker-Day-31_31_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:16f7600c138c86c88e74e3c9c108e47f290f83f98b6a5a654faaf6d8858cdd3a +size 242662 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiPicker_null_EmojiPicker-Night-27_28_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiPicker_null_EmojiPicker-Night-27_28_null,NEXUS_5,1.0,en].png deleted file mode 100644 index aaab9d35c1..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiPicker_null_EmojiPicker-Night-27_28_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5ea42394330d37a0aa5ba8940e023fc4454d902caf6d265adec0e11c8a34d85f -size 187744 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiPicker_null_EmojiPicker-Night-31_32_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiPicker_null_EmojiPicker-Night-31_32_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a62ac20738 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_EmojiPicker_null_EmojiPicker-Night-31_32_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0baf11279836eaa6ff80568048892653d221bed15f41caaf44658d0f3ea3777 +size 244028 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Day-43_43_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Day-43_43_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 5b8f0832b2..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Day-43_43_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:726cb736f33634d3b7efd1ae01b5f7808ffba8933ceda115b3b368070f26ede7 -size 5474 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Day-46_46_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Day-46_46_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ea3e0fe358 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Day-46_46_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:738755ab5abca5b0f0b70eeb3e01fc6dccfdfafc187d167b423f75251c186b69 +size 5426 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Night-43_44_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Night-43_44_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 1fffb2a53c..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Night-43_44_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:99d85977724a11c52831391fb2d1bcb5bcd2fd697a9e1073b067cfc25a9cbcc7 -size 5437 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Night-46_47_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Night-46_47_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..82e43aad56 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Night-46_47_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec86192f5d06d925816bb58f6e031996a6cfcc2eac05d24f899c5f19f1ca2819 +size 5397 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Day-28_28_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Day-32_32_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Day-28_28_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Day-32_32_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Day-28_28_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Day-32_32_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Day-28_28_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Day-32_32_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Day-28_28_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Day-32_32_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Day-28_28_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Day-32_32_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Night-28_29_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Night-32_33_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Night-28_29_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Night-32_33_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Night-28_29_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Night-32_33_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Night-28_29_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Night-32_33_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Night-28_29_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Night-32_33_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Night-28_29_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemAudioView_null_TimelineItemAudioView-Night-32_33_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemEncryptedView_null_TimelineItemEncryptedView-Day-29_29_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemEncryptedView_null_TimelineItemEncryptedView-Day-33_33_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemEncryptedView_null_TimelineItemEncryptedView-Day-29_29_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemEncryptedView_null_TimelineItemEncryptedView-Day-33_33_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemEncryptedView_null_TimelineItemEncryptedView-Night-29_30_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemEncryptedView_null_TimelineItemEncryptedView-Night-33_34_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemEncryptedView_null_TimelineItemEncryptedView-Night-29_30_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemEncryptedView_null_TimelineItemEncryptedView-Night-33_34_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Day-30_30_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Day-34_34_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Day-30_30_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Day-34_34_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Day-30_30_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Day-34_34_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Day-30_30_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Day-34_34_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Day-30_30_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Day-34_34_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Day-30_30_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Day-34_34_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Night-30_31_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Night-34_35_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Night-30_31_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Night-34_35_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Night-30_31_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Night-34_35_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Night-30_31_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Night-34_35_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Night-30_31_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Night-34_35_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Night-30_31_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemFileView_null_TimelineItemFileView-Night-34_35_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Day-31_31_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Day-31_31_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index affcb49660..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Day-31_31_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c40273c36eb1479f284e75fa91d4a75b1ae97edd0242dda37a2d4a8f10394928 -size 138700 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Day-35_35_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Day-35_35_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b4ea0fc251 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Day-35_35_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:301b6cc63cc5e075aa70e0d4d67da5a3fd96049f834f5d04665e905de6860d89 +size 123525 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Day-31_31_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Day-35_35_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Day-31_31_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Day-35_35_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Day-31_31_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Day-35_35_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Day-31_31_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Day-35_35_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Night-31_32_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Night-31_32_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 3317b558bc..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Night-31_32_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9765d4c28463df920063a46f3aaaa29df816ba44c424ae4fac6c9e2380cb64ee -size 138717 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Night-35_36_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Night-35_36_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..1bab8a7a77 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Night-35_36_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d03c9055f17ecba10787da59bc7d598b9b397c52f70f930a4603c58bb26b2d60 +size 123995 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Night-31_32_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Night-35_36_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Night-31_32_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Night-35_36_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Night-31_32_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Night-35_36_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Night-31_32_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemImageView_null_TimelineItemImageView-Night-35_36_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemInformativeView_null_TimelineItemInformativeView-Day-32_32_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemInformativeView_null_TimelineItemInformativeView-Day-32_32_null,NEXUS_5,1.0,en].png deleted file mode 100644 index bd2a95e805..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemInformativeView_null_TimelineItemInformativeView-Day-32_32_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:59754add1de8ada9dab57df474147234d9978b3950a36b299150dc161bbfae6a -size 5815 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemInformativeView_null_TimelineItemInformativeView-Day-36_36_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemInformativeView_null_TimelineItemInformativeView-Day-36_36_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ef2c779b95 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemInformativeView_null_TimelineItemInformativeView-Day-36_36_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7369daa5512bfb1de2fab6c34bab10a670dfb3b052649e080c07923a409bc9a2 +size 5805 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemInformativeView_null_TimelineItemInformativeView-Night-32_33_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemInformativeView_null_TimelineItemInformativeView-Night-32_33_null,NEXUS_5,1.0,en].png deleted file mode 100644 index b2fef1c697..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemInformativeView_null_TimelineItemInformativeView-Night-32_33_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c48859e45109cb1d4bb733dcbaf3014bf8a3f3edbc5746865f39837e9903c598 -size 5768 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemInformativeView_null_TimelineItemInformativeView-Night-36_37_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemInformativeView_null_TimelineItemInformativeView-Night-36_37_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4dd3825082 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemInformativeView_null_TimelineItemInformativeView-Night-36_37_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c6dbbb4899659912a09c9ff7a4836ac08e17a3fb51ebfab2e6cddc4395da0afe +size 5779 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemLocationView_null_TimelineItemLocationView-Day-33_33_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemLocationView_null_TimelineItemLocationView-Day-37_37_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemLocationView_null_TimelineItemLocationView-Day-33_33_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemLocationView_null_TimelineItemLocationView-Day-37_37_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemLocationView_null_TimelineItemLocationView-Day-33_33_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemLocationView_null_TimelineItemLocationView-Day-37_37_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemLocationView_null_TimelineItemLocationView-Day-33_33_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemLocationView_null_TimelineItemLocationView-Day-37_37_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemLocationView_null_TimelineItemLocationView-Night-33_34_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemLocationView_null_TimelineItemLocationView-Night-37_38_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemLocationView_null_TimelineItemLocationView-Night-33_34_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemLocationView_null_TimelineItemLocationView-Night-37_38_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemLocationView_null_TimelineItemLocationView-Night-33_34_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemLocationView_null_TimelineItemLocationView-Night-37_38_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemLocationView_null_TimelineItemLocationView-Night-33_34_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemLocationView_null_TimelineItemLocationView-Night-37_38_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollCreatorView_null_TimelineItemPollCreatorView-Day-35_35_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollCreatorView_null_TimelineItemPollCreatorView-Day-35_35_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 1ac340b172..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollCreatorView_null_TimelineItemPollCreatorView-Day-35_35_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:09440b685e219b4a5f5dc78d2694707d7eeb1905d6fdea6395e3dcc941482ac5 -size 51846 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollCreatorView_null_TimelineItemPollCreatorView-Day-35_35_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollCreatorView_null_TimelineItemPollCreatorView-Day-35_35_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 540d962b0c..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollCreatorView_null_TimelineItemPollCreatorView-Day-35_35_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ae8d6b561d358c7ebccae2da4281f3ea1b4d433f52de3c3b2008c1208c0c8bd7 -size 54037 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollCreatorView_null_TimelineItemPollCreatorView-Night-35_36_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollCreatorView_null_TimelineItemPollCreatorView-Night-35_36_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 7c14f911dd..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollCreatorView_null_TimelineItemPollCreatorView-Night-35_36_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a0cd0936ace9bac190f21680bf339cc23839e5f5d241522cb8f328b2db0887f6 -size 48293 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollCreatorView_null_TimelineItemPollCreatorView-Night-35_36_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollCreatorView_null_TimelineItemPollCreatorView-Night-35_36_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index e14dee1d70..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollCreatorView_null_TimelineItemPollCreatorView-Night-35_36_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cc96f4b704396e0931a08a968139ee9d586cd1e14358b546361d11bc91e7bab5 -size 50373 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Day-34_34_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Day-34_34_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 18764bef11..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Day-34_34_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:188c362ebd8bc32a47b66a080331db7643cd97714f2d4e952d7bec8c11520dcd -size 49026 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Day-34_34_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Day-34_34_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 1117d239f3..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Day-34_34_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9de6ab591cb02f6545218a2606d031f4f93c82e71e99523eacaf77ffa78fadb1 -size 50940 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Day-38_38_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Day-38_38_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..338b81a12b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Day-38_38_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27550a0227e993f13e343219973e50ad6902737416e90c41b7a1916723c32e08 +size 49025 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Day-38_38_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Day-38_38_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..9a087a954c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Day-38_38_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0435109c519a31d671bd5cf85425574a10909733e21dcc46ae17e2a53d748b11 +size 50938 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Day-38_38_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Day-38_38_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..efff51686d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Day-38_38_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8209a79e5d20ef58a2e947617b308386d4868799c87039d40fe7c89a68925f2b +size 51842 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Day-38_38_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Day-38_38_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..19c589c630 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Day-38_38_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb71e2b69f31bdc3ee7bf17175f099c3838e311978bfed78ce40e6ad98c871e1 +size 51991 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Night-34_35_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Night-34_35_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 8bae8c65fa..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Night-34_35_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b4b00894025844927932e790a1738c85cfbb61e61a81dfbbbd7e342f38f40b99 -size 46061 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Night-34_35_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Night-34_35_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 5f8c7437c9..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Night-34_35_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bd88ed3aeb9a20f148e914c4a5d4554220a1fecd8e8a8fe87400de78ff4bf248 -size 48237 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Night-38_39_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Night-38_39_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..378fa05efd --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Night-38_39_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c73a0f3bc8127e9d1491a1056975fa381a0a29aa84f338e14d3c67a8e734918e +size 46059 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Night-38_39_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Night-38_39_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6cdba0f6ce --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Night-38_39_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d320451b7608100f093bb140331fd1fcdecea6c0dc1e721acb0be9045a6b4df +size 48238 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Night-38_39_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Night-38_39_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..33f25a7736 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Night-38_39_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2f174ed9f51808a295fd8b748faa0709fb3ef45ebe9cb7cb093f971edbb11e0 +size 48291 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Night-38_39_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Night-38_39_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..029948fca4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemPollView_null_TimelineItemPollView-Night-38_39_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e7ff7a9241609b8990ad3353947e550607eca4d8809c0ace796a3cd3dd2ff079 +size 48382 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemRedactedView_null_TimelineItemRedactedView-Day-36_36_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemRedactedView_null_TimelineItemRedactedView-Day-36_36_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 70d22913aa..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemRedactedView_null_TimelineItemRedactedView-Day-36_36_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f809cbf4c3fb897ea81d1cc4742178e847af1db758064cdd3e9cab081ac6a915 -size 8562 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemRedactedView_null_TimelineItemRedactedView-Day-39_39_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemRedactedView_null_TimelineItemRedactedView-Day-39_39_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ec89f829a0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemRedactedView_null_TimelineItemRedactedView-Day-39_39_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cf01c84f2a81cd732e2a8fb50c07a7f0420ba382df03301e8ca45b3c1beca714 +size 8571 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemRedactedView_null_TimelineItemRedactedView-Night-36_37_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemRedactedView_null_TimelineItemRedactedView-Night-36_37_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 198121784d..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemRedactedView_null_TimelineItemRedactedView-Night-36_37_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fd335a3ce4e931b6e7d123f9f4a8f9acb285b958f03c25979a793874c6176d65 -size 8471 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemRedactedView_null_TimelineItemRedactedView-Night-39_40_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemRedactedView_null_TimelineItemRedactedView-Night-39_40_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..169c3c9fe8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemRedactedView_null_TimelineItemRedactedView-Night-39_40_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0fc2c67284ce261f241f063dc81b604f4bac5bd4b8872efd66478f27814fc75c +size 8464 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemStateView_null_TimelineItemStateView-Day-37_37_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemStateView_null_TimelineItemStateView-Day-40_40_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemStateView_null_TimelineItemStateView-Day-37_37_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemStateView_null_TimelineItemStateView-Day-40_40_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemStateView_null_TimelineItemStateView-Night-37_38_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemStateView_null_TimelineItemStateView-Night-40_41_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemStateView_null_TimelineItemStateView-Night-37_38_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemStateView_null_TimelineItemStateView-Night-40_41_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-38_38_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-38_38_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 35d3a52fd6..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-38_38_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1ea2576745ff79c9fb37a54ecf9d84c0c6849404f108bd695d8332e42f8e2082 -size 5916 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-38_38_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-38_38_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index c20baeabbd..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-38_38_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0a7dbee314d26d3dceef67818374250c201b68b889e2e4a14224405e58737331 -size 7879 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-38_38_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-38_38_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index d9fcae4725..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-38_38_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:afc0f5d66d83f219c220d8136041abc70776d171bc9f13e3dbe3cb39740a2136 -size 6199 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-38_38_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-38_38_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 1f27fb6dc2..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-38_38_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8d6d5765db221efdd927da30a10dafe6f4c5851c44539a3d90efe84601cf5adb -size 8168 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-38_38_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-38_38_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index 87ba9657eb..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-38_38_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fd7b233c6973e3a048c1abe50f09d9740dd4a6da0b44b1d56a9b301d9cb1f9ec -size 5518 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-38_38_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-38_38_null_5,NEXUS_5,1.0,en].png deleted file mode 100644 index 1b17634a4d..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-38_38_null_5,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a2cdb0b10daa1ab865334f8447fc14a30a0c79a43be5d2c94f5da8d981fa0187 -size 7543 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-41_41_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-41_41_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4c83c2fa37 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-41_41_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7c32500fa620506fb7cbf912793e5919ae2c4da8a6acf83ecd2ad344d95ea4be +size 5937 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-41_41_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-41_41_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..816d153fcf --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-41_41_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4256e9443e028d232420062300ee0c36c540b7430fa3e7ccea71304526532c97 +size 7770 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-41_41_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-41_41_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f3d7372ed5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-41_41_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5684ce9c6111eed345950e036126271bf9cabf535ecea54bf81c370e941051f5 +size 6247 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-41_41_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-41_41_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ead12875a6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-41_41_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:edca1a2ee9211e0595c45f9fe6c4c830c27e5e4edb75d3bf9fb7b3514b8a91fc +size 8042 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-41_41_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-41_41_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..563ca0283c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-41_41_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6279e616bd3f912654774495ee38914b15f5ab89449e49e6d03509a7f719ae9e +size 5515 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-41_41_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-41_41_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..47e34c1fa6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Day-41_41_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78c1bd81fb40b7adc968e0706bc392e02848a5bac536ada5143f27efed15fb77 +size 7445 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-38_39_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-38_39_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index ef5478d084..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-38_39_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a490c7c4b58885a06fd89f2cbc8a832e50ba58fba1483e327796a3ef5b6d0c23 -size 5851 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-38_39_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-38_39_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index f070b75927..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-38_39_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:57c45ab46932c22290987266d5bdfbe392c1c6301803e7176545e3b1ed74b3d7 -size 7549 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-38_39_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-38_39_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 99e5127412..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-38_39_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b38b8ec214dae0b5e0b05c5fd2c315a73004bbc5a607440a521587ff05780120 -size 6110 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-38_39_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-38_39_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index ba40afcfa7..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-38_39_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0b6708c60d03e4a0005244a7b7773adcf4f9cfbeb54f2d96c41a80c3ad32b262 -size 7759 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-38_39_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-38_39_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index 8557a38bbd..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-38_39_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b2ef496a01c45907932fd160d4b906bc90b929d90978466a7ae96f856c0edcb8 -size 5484 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-38_39_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-38_39_null_5,NEXUS_5,1.0,en].png deleted file mode 100644 index 4a2bf026a5..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-38_39_null_5,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4a18f7a783f1b55aa8678e669eabb6be46f45438a3737867e171c889fda6e001 -size 7222 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-41_42_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-41_42_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f30213bb7d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-41_42_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61912b822c573d1cbb730dbdada9825d4508f04f947473044ff1e5344e113c11 +size 5846 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-41_42_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-41_42_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4183680212 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-41_42_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af0e2591d88eb92b12d910e6efa29f1673f0702bf0ab56fa9179ad39f9168ba4 +size 7399 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-41_42_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-41_42_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d4bab61c68 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-41_42_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f34118dc5296d9b564d932bbf0a91944e31cafbd4135331a5b13c9a3034df4b5 +size 6083 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-41_42_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-41_42_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f6a719f324 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-41_42_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d060ccab47110ecf584517cf922b1cca0facc705a70ab5d29fc40354b50ac184 +size 7681 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-41_42_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-41_42_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..189df55aaa --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-41_42_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e72e218bee1fc66cf8c1f9cf2c2ba9c5654b5738b560ea5728084996345cba4 +size 5471 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-41_42_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-41_42_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..32610bf698 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemTextView_null_TimelineItemTextView-Night-41_42_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79a9c442b826783e3c06ea3dac28f24e836a715da27c70e47cfa2296da0df270 +size 7127 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemUnknownView_null_TimelineItemUnknownView-Day-39_39_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemUnknownView_null_TimelineItemUnknownView-Day-39_39_null,NEXUS_5,1.0,en].png deleted file mode 100644 index d9ffa583ae..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemUnknownView_null_TimelineItemUnknownView-Day-39_39_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:14e83525a099c960c6b4cde848df4a1572b72859f1566565a24cc50417c2fbb7 -size 8726 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemUnknownView_null_TimelineItemUnknownView-Day-42_42_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemUnknownView_null_TimelineItemUnknownView-Day-42_42_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..035c79ac05 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemUnknownView_null_TimelineItemUnknownView-Day-42_42_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2087d9a1e15d0432944948a528cd703309412c389a3f6220280ca453b6f5e42a +size 8719 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemUnknownView_null_TimelineItemUnknownView-Night-39_40_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemUnknownView_null_TimelineItemUnknownView-Night-39_40_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 4f87b4a5ff..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemUnknownView_null_TimelineItemUnknownView-Night-39_40_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:21241d8b7dc438f151689cb6f65125b1deb5c005152b93dd3a7c1f6c6452f021 -size 8644 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemUnknownView_null_TimelineItemUnknownView-Night-42_43_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemUnknownView_null_TimelineItemUnknownView-Night-42_43_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..787caf5f0d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemUnknownView_null_TimelineItemUnknownView-Night-42_43_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:22a71a3d7e9c7141cee337a8f55b7115449aa3ce797e224ff730ed88883c228d +size 8640 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Day-40_40_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Day-40_40_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 5361cd1a24..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Day-40_40_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:06617cda0f93ce0ea26b2a77a243371cdaf4f9a2956d8159ecb7196f7f6fe082 -size 139282 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Day-43_43_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Day-43_43_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..204de568ae --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Day-43_43_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4fe06708cd1f1e49254779bce07f6751792a94fa9065882385efad1ac8663c2 +size 124159 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Day-40_40_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Day-43_43_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Day-40_40_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Day-43_43_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Day-40_40_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Day-43_43_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Day-40_40_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Day-43_43_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Night-40_41_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Night-40_41_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 4556fd9980..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Night-40_41_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7d444f76e3eed1fe8291a25b24a8712427747a728270244cfa3842260b9c1b2d -size 139335 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Night-43_44_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Night-43_44_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..3caedb9fe3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Night-43_44_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b1c09f5b8c68f493a04541a7ce77ca33d98c41e56c020e355dfc4419406592ae +size 124700 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Night-40_41_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Night-43_44_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Night-40_41_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Night-43_44_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Night-40_41_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Night-43_44_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Night-40_41_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVideoView_null_TimelineItemVideoView-Night-43_44_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Day-42_42_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Day-42_42_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 7bda39becb..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Day-42_42_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cb6bf3cc0868def05297678d308ecfda99f36239bda2ad67a144c5054cd09f19 -size 45680 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Day-45_45_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Day-45_45_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2f20c70358 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Day-45_45_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:298c750f283995af2552ac72f86fe4747287a781b8c25e890616efd08b4ae54e +size 45436 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Night-42_43_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Night-42_43_null,NEXUS_5,1.0,en].png deleted file mode 100644 index b916f8b832..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Night-42_43_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5f9f5fbbaa510904e7f21fee456789ba9154dc7fd927caf5719daf4bce49519d -size 44517 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Night-45_46_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Night-45_46_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..45663f47d9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Night-45_46_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6871183e7bba8fc9574dc838ef10626a7d573d21cea6d0a1a4219ac928c8697 +size 44211 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 82c25db6e1..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9b7baba714a54c9be4611677b938705277e2f967c7361af4fd8e16651b732cc0 -size 5729 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_10,NEXUS_5,1.0,en].png deleted file mode 100644 index 0ca95a9c4a..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_10,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:111c3aafacfbb3fd16421c74eae2b3c6ba7c2f6f3b2203c0ab91a226e5015e2a -size 9529 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_12,NEXUS_5,1.0,en].png deleted file mode 100644 index 95680dea95..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_12,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:10966cc6564e45c0d65cdfee2564b7367defc1f673bf62c02291d427655d557c -size 9601 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_13,NEXUS_5,1.0,en].png deleted file mode 100644 index 9bef5959d3..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_13,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0be1de85e0038df89677884557f9d8a35af8acba29749879469e01ddf095aca7 -size 9933 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_14,NEXUS_5,1.0,en].png deleted file mode 100644 index 5bf6d017a8..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_14,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ab61d047110a59bfbf327980f08572d9f6a48854a27e9ac256956ac465733043 -size 9884 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index ee35d328f3..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:326b07336560db487616f189e3e39dc5605cabd06e15f836fd153c503ab6fa9d -size 5577 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 0c4317b30f..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6c70f961cc6ca573216019b3f0fabc18ae05d8bab9bb5719abca1021b9d2350b -size 6089 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index 73b1551706..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:49066b0adf53941af950614f1090de66e372f1550d3f0a2b8f88d1bf255f7e3b -size 6055 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_5,NEXUS_5,1.0,en].png deleted file mode 100644 index 733df194eb..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_5,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bcb3a451a7ae4271fc2acbda8ba5f40a21167bb36a81d14b9006c68baddff3c0 -size 6790 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_7,NEXUS_5,1.0,en].png deleted file mode 100644 index 7f1bd43732..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_7,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:da00e2729bcd8ac55de08a2ff2e101e6746a64a5fdc07c8ddbb8297543dc493f -size 6663 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_8,NEXUS_5,1.0,en].png deleted file mode 100644 index c18ddb3953..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_8,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d44ed3c1ff4607171e21c580f337ca0a5664992fb90d2072b511af8e72ac8353 -size 7070 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_9,NEXUS_5,1.0,en].png deleted file mode 100644 index e3ec028872..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_9,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:098a61f028e90cdf8b7e708793ebfaab20cde82a23ea117acb901469947dc713 -size 7335 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8f1df4bc79 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:089d6d749fa7c6baf92efc11016bec4559f99cd104e97903da8b1f467be52e8b +size 5725 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_10,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..71c01804f4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_10,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fb2f64e9472023452f062c3f3aaa780a547d70b0f3b040042bfaefafb097ac14 +size 9515 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_11,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_11,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_11,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_12,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..1f07d497c6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_12,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9df0b76dd3d46022243b03d5c5f1accabf2345b9412d2cf1f9d3e6e7d5bb18f4 +size 9565 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_13,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..3cc13a71e9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_13,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:260e882a9a068b5a81a251cfd20c6c99473fc7d905551f1e8de12d0d6165538d +size 9919 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_14,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..aa06d91df5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_14,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec0433abe5363115cdbec559ddba1cc33aeb3227218d07b988c7ca52c9effe2c +size 9849 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6496375c3a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e0772d6c4f9b994d2b32de900b80faccb5598774db46fc161009a4774d6efd2 +size 5531 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5786c505b4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:438d24d36310528695e0f79d9a515a599ae27fa028c2290477a0df622240ab87 +size 6077 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8fb23bc5a6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:666fa4b85b3d76b8992b46d50ee0d5ce45e10b83ef50c7cef2a82fdf5fb1ff60 +size 6017 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..02fc779941 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9845fd892c9913c25b8bd49d0a4a67ba5d691475a31dd5a16937878bfe945ef3 +size 6777 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_6,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_6,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_6,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..9d622e9a3d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0f92bf5702aa5c05be66bfd03f2a4437385abe20e8d397c21aaa329018f38ee +size 6617 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c6493ffa03 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:212d6a2efc1d6464086a4fbf60922efc6285705d0365752e5d264c1b19c1f97c +size 7063 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_9,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a100d9b639 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-44_44_null_9,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:219a6f8e4032493f96a4df0e1072ad8ddfcd5d77c15878f26c4ffd462417b2d0 +size 7303 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 2f0181bf55..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6780dcb4537aed0168fecc8a7df77d17b38d57f5e2fa697a353728e9bd28aa59 -size 5697 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_10,NEXUS_5,1.0,en].png deleted file mode 100644 index a71c8488c8..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_10,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a6d969780e06a96c68cc547f7731d839d4eb3855bc39bacd7e7fc95692bc8544 -size 9324 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_12,NEXUS_5,1.0,en].png deleted file mode 100644 index aec364a0e6..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_12,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ad2ccde01384563471412843a48180b7acb3ba9bbedb9af60f96d403e564073d -size 9566 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_13,NEXUS_5,1.0,en].png deleted file mode 100644 index cb45fe4553..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_13,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e32db0169cf50c2120a16efea3cb2b0f8a72f81ddce7a303c13bc2bf38c487ff -size 9653 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_14,NEXUS_5,1.0,en].png deleted file mode 100644 index 18a1430f5f..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_14,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:93fb4175b173f065af362386e6c9026ce96fca5aee602d0dba9782fcbc0febb1 -size 9592 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index df1c6e821a..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a0a6b88a5d7f001309dcb03da6151ac2949705b32a19301660a24fe02dc6fcdd -size 5562 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 9a70f58bb9..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2f8562e1e599e16ecc05ec0471de70b9de9a88b347f622bc723f21cb5379b102 -size 6033 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index 5c9912a57e..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cac1f8a26b53bc6867ed54095de6ee73b840fb0dccf056f99678bfa9df126d7b -size 5993 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_5,NEXUS_5,1.0,en].png deleted file mode 100644 index a57555c928..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_5,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:12ad14137042e62096d78434481b73a9e8668a617f5b465ee97695fc0a008382 -size 6795 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_7,NEXUS_5,1.0,en].png deleted file mode 100644 index 32b8d33a13..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_7,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fd21f1c5ad2616b698a386d78d5efe0ab40aa1959382ed2a568c10441d093a8f -size 6692 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_8,NEXUS_5,1.0,en].png deleted file mode 100644 index dbee328561..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_8,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:07f52161378dfab92ffb76b860680390ccb5dfcc05347c5fd99504c806dd3210 -size 6930 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_9,NEXUS_5,1.0,en].png deleted file mode 100644 index a0f8df916c..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_9,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:39905ed8db6cbf7ba5400f0b0455785703667ce9c47d1dfb59db217f40f4d507 -size 7162 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..495a2f9cde --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30fab3dccadcdbb7c6cd624cb6a807e47c5472f5395f043a7006341f0b5d6f9d +size 5672 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_10,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4e411f48c6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_10,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c266c2b071139a966d2f68dde56bbc0076c6e66ac3015d86963295db8e880550 +size 9290 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_11,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_11,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_11,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_12,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..fe41f74e9e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_12,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4b748360d95b3898a471c38ca6f1440a6b83590ebd297abdeb59539e08f25a0 +size 9535 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_13,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8d0842cfff --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_13,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:01a918a5cbe544f7f0536373b94e41673fa0dfb70a1616aebdab385b9f5e6b74 +size 9635 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_14,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..67a7fd4925 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_14,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:586d897dc6ecee0d11d48d3fd15b62d1e427094ba4393d1a5d47646fa814b7b9 +size 9555 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4b5de27180 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c4578011963fa9d2d0a6c5e7278cabf301450e8f2b350ede3cf0b7a175657dcc +size 5519 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ce8785b979 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b3c5a7bfa1564787ee64d967e77ca84793f365e3a6d77beeba19106192eb26a3 +size 6011 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e57e256001 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5fbadfc2206661cd661796a1afb42fd7e29a7b3b170b95eb022fadb258be1334 +size 5959 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ce0b0d7052 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:19f96fcad36afd93ea830007f3a599f726fa72d91ea97749c5a21b4c08454423 +size 6773 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_6,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_6,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_6,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b744ff24ea --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7aabc26dcb237a1d87c8f888812643e472cff9f082a5fb18d800698762becb63 +size 6636 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..aa42c543f5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:11a48af25f2b2f656a3e6ce323d2a4b392cf180555f06423c2ecc0044ef9de22 +size 6901 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_9,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b8da66b39c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-44_45_null_9,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ce011eeb89741dcdb9c98edfd3f0daf4268e0e02b013caf12fec9b4a33d20b05 +size 7132 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Day-44_44_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Day-44_44_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 2d34a3f1ed..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Day-44_44_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e387949352779b84674d3dc23760305b49c197342e3f90fa8ca8576debfa3201 -size 25382 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Day-47_47_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Day-47_47_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..1935bc644c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Day-47_47_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:910e28b2dc89b08b99b2d692d10bea6b79c85749f804842d63177d5380283006 +size 25411 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Night-44_45_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Night-44_45_null,NEXUS_5,1.0,en].png deleted file mode 100644 index d2a061dd54..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Night-44_45_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:55b5ec0b3483805b0898a39fcc6d9d20adcebdc03a1926eaa52c5f34f6484be6 -size 25026 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Night-47_48_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Night-47_48_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..66c090cbb2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Night-47_48_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9af3bd8ae926ca6bf67103aad707c28e72138c564126eeb832bf589fbd2f81e +size 25033 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index a0d7f77db5..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5c576c7288e434193189c23f75f92b9569c65c878c4dd9e9dc9b3a44af43f792 -size 5555 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index fb9b2bce5e..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e742874a48ffbfa5322d01537fbd49cacf7e42b98b87eb76bf1459a14fb82ac2 -size 6353 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_10,NEXUS_5,1.0,en].png deleted file mode 100644 index 1fbf4be54d..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_10,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b14b7052c2110b2f692472ab8b33631486ab863037106d59e39480203c6432b1 -size 5375 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_11,NEXUS_5,1.0,en].png deleted file mode 100644 index a1eaf816e8..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_11,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4108d9a249b5bb1c0c88dfe54f9df0f5f7b3a5777d6fd5e00d5fd5e41608cf09 -size 21917 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_12,NEXUS_5,1.0,en].png deleted file mode 100644 index 565ea280dc..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_12,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c41c75f17e291e0be64551202fac5147a0174da7634c1e3645842402f61ae4d9 -size 5111 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_13,NEXUS_5,1.0,en].png deleted file mode 100644 index c71367390f..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_13,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4ec3da0af91751e2d2e949da2f8cb94f4805e7747ba5403afd736a393d58447f -size 7145 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_14,NEXUS_5,1.0,en].png deleted file mode 100644 index 2161c84892..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_14,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1d8c35c24f0b725c56fc54eb025a4c42c69cf3acbad37cc550b0b950812cb20b -size 10103 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_15,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_15,NEXUS_5,1.0,en].png deleted file mode 100644 index bbb366fac5..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_15,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:43891e66bae441f53f576c0affae0f36c98399f9bfe1d034f0db6ae218da8e47 -size 8233 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_16,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_16,NEXUS_5,1.0,en].png deleted file mode 100644 index cf3f824fd9..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_16,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7240b9666592f3627bb2e944cc83ab31eface8139c5c98dcc6c1ad9e7ca9b8b6 -size 11285 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_17,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_17,NEXUS_5,1.0,en].png deleted file mode 100644 index f9d723a20f..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_17,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5b1c4557ffd800c0eca4e41e04e1aea9c6cd0ef94b70f1704352fe8070d8fff1 -size 7507 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_18,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_18,NEXUS_5,1.0,en].png deleted file mode 100644 index fc801539d6..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_18,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6859208aff5071a73cd5c8f9f5fe4437abd793fb564bb4dfc4a3a590a8262a1e -size 7946 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_19,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_19,NEXUS_5,1.0,en].png deleted file mode 100644 index f3c121e692..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_19,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1587014aa4cef2ade84e55ab0a9209d0f10b8706164d8e8b28933554dce24d59 -size 14386 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 143d985235..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ac00bcc3150b97a628a03996ce273f68bf5c8ac9db014a7039e3510b4b307726 -size 5675 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_20,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_20,NEXUS_5,1.0,en].png deleted file mode 100644 index 3312595f8a..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_20,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4c0a959f003d8eedf2a4b9284f63ea77a608e272d0dd8e564bbbbf3686c88758 -size 11572 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_21,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_21,NEXUS_5,1.0,en].png deleted file mode 100644 index 5a50bf3ace..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_21,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d3a3ffdc5062cea8c3226df50b0876a3ca02045a19e05974b95e5ac83b813f39 -size 11502 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index ee412cae4a..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6f5585d6b47d201c1d3d8b1e330b6ebe2bc23811a72f4454add2f563cdeb41f6 -size 6193 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index c991c0d165..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:05bb4680a0f8afb75b8f32292b54ccc14aac2e868bc122e110c07d4dc2b30915 -size 8596 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_5,NEXUS_5,1.0,en].png deleted file mode 100644 index 5205bb76ba..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_5,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3e3588c936172c143fb195a9113d293858c8bd3678b24a88ca64dd9f3c2cb7a1 -size 8554 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_6,NEXUS_5,1.0,en].png deleted file mode 100644 index 68248c360a..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_6,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3cb4d5f28b52e87ce74ca667a330b3b706d1373e6a23c9a9f6a82be9a0460adc -size 5494 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_7,NEXUS_5,1.0,en].png deleted file mode 100644 index 5f22f73c3d..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_7,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2c169da272ac92d2f9ada1eb90a3c644c54e6788141b4d0ee07c49a59972f567 -size 6455 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_8,NEXUS_5,1.0,en].png deleted file mode 100644 index b71a68a418..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_8,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8803bbcece24e3c1dd3eed4055fcb8b8aa1a3d3d06feb5c37813499808c7faf0 -size 6481 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_9,NEXUS_5,1.0,en].png deleted file mode 100644 index fc7fbf6896..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_9,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7f17122c670bd4e9e8f419d40820d29ac75d3f7f0e5d6d1b4e819ddacfe40057 -size 5922 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 10306cd011..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7a6c94b36f43c47dea5d2b06b85ec2008b445ee54bffb45be812c3749e7c8fff -size 5527 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 060232bac3..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d8e5ff9c6ba12aee23ad547b71666096f5a184ad94877135587d77e1ed263c99 -size 6240 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_10,NEXUS_5,1.0,en].png deleted file mode 100644 index d0eeef63a1..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_10,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f91e0c0c857b7e97a31c01a12947843eca7af664d23dec336e30d386575cd955 -size 5345 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_11,NEXUS_5,1.0,en].png deleted file mode 100644 index dd72a20d7b..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_11,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5a3f45d7dffec016578932cfd230bd57a649b2e725943f81c4d157a958826abe -size 20718 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_12,NEXUS_5,1.0,en].png deleted file mode 100644 index 857c4a7154..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_12,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:98992d574b00fd9b168541d7826d2290ffb5598ed31aa540ef86eb05b8cad6a0 -size 5115 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_13,NEXUS_5,1.0,en].png deleted file mode 100644 index b1064f1c42..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_13,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:12cb95c208d0c536200e75e72476f47bd584a039f47a11be86de607cfdca24ff -size 6836 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_14,NEXUS_5,1.0,en].png deleted file mode 100644 index 0c99ce1967..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_14,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:22011955b67135850138578a5d0c0211541ec1cb2d422487d149c05d2da5537b -size 9513 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_15,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_15,NEXUS_5,1.0,en].png deleted file mode 100644 index 90e662cf04..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_15,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:43e01f8600b71c4b7d3437a89331cfa2723049b10500b74902b17e8bae82ecaa -size 7984 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_16,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_16,NEXUS_5,1.0,en].png deleted file mode 100644 index 2beaddefc7..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_16,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c9e94e9e5b42dc50ef2958872a2307bb5411e25ab8bf3e696fcc1948d04a602a -size 10588 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_17,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_17,NEXUS_5,1.0,en].png deleted file mode 100644 index 8a641258e9..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_17,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9e3e6f73a4ca3e5630d498541e6ba4b62505652e35f8368bda9dbc5cce618456 -size 7367 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_18,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_18,NEXUS_5,1.0,en].png deleted file mode 100644 index dcfa4bfadc..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_18,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:517d6b30065d903215ab57446f3639c3876d85b85bff4679ffae4c2132775dda -size 7567 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_19,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_19,NEXUS_5,1.0,en].png deleted file mode 100644 index 136afbb481..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_19,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:945db7d76ce34a2285e8d083f8c9651204af59aa800ffda4587f854676f717b7 -size 13529 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 291dd9e57f..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:31d2f62b114e1b41d3c7683b2c08e7d70931139e87cf76341d03fb488add4838 -size 5662 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_20,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_20,NEXUS_5,1.0,en].png deleted file mode 100644 index 4dd2a2088c..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_20,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:51155f13d3795198a301788288351c386e4d509e6ebd3ed103e743762e54a426 -size 11208 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_21,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_21,NEXUS_5,1.0,en].png deleted file mode 100644 index 5955c38495..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_21,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6199ff6234486bfe7ed87438298086e09e51dcbe4b30a71fa494764f5b3db14c -size 11142 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 4951caddd8..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d1dd0872ea0786c5b9c037f05aef2c43d278e450558cb2f2245d76a8051f4836 -size 6089 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index 37bca53ea5..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:63953e0cca146083474b69eef7ac861391e111c319ca027e8222dc28cdab7032 -size 8075 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_5,NEXUS_5,1.0,en].png deleted file mode 100644 index cd99dfc019..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_5,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d5d4c973178bf5793e1231abdc1837aecd16bdb227d0b5128a36db8e1d36290c -size 8009 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_6,NEXUS_5,1.0,en].png deleted file mode 100644 index d765ec297d..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_6,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aa2431a475eecdf0980bd7bf237e30d8b2e1b98170f42474d8297286386342c0 -size 5433 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_7,NEXUS_5,1.0,en].png deleted file mode 100644 index 0c5e985c08..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_7,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0179e7d8b68b2bb130e0c6c8db6adde979c724d293918822e6807e9eef627135 -size 6300 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_8,NEXUS_5,1.0,en].png deleted file mode 100644 index 29e881112d..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_8,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:506c86795d7940d0cddef77d73eb225b31c9204fc19b81078d95eab71f6d9499 -size 6301 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_9,NEXUS_5,1.0,en].png deleted file mode 100644 index dce38442ab..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_9,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dc0d2daf9d4bda4005f01c604bdc6688673e9a50d5e92ca1668c481327529282 -size 5831 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Day-46_46_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Day-48_48_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Day-46_46_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Day-48_48_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Night-46_47_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Night-48_49_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Night-46_47_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Night-48_49_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-50_50_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-50_50_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-50_50_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-50_50_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-50_50_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-50_50_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-50_50_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-50_50_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-50_50_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-50_50_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-50_50_null_5,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_5,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-50_50_null_5,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-50_51_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-50_51_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-50_51_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-50_51_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-50_51_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-50_51_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-50_51_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-50_51_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-50_51_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-50_51_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-50_51_null_5,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_5,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-50_51_null_5,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-49_49_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-49_49_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-49_49_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-49_49_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-49_49_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-49_49_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-49_49_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-49_49_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-49_49_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-49_49_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-49_49_null_5,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_5,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-49_49_null_5,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-49_49_null_6,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_6,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-49_49_null_6,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-49_49_null_7,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_7,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-49_49_null_7,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-49_50_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-49_50_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-49_50_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-49_50_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-49_50_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-49_50_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-49_50_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-49_50_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-49_50_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-49_50_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-49_50_null_5,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_5,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-49_50_null_5,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-49_50_null_6,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_6,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-49_50_null_6,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-49_50_null_7,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_7,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-49_50_null_7,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-49_49_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-51_51_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-49_49_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-51_51_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-49_49_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-51_51_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-49_49_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-51_51_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-49_50_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-51_52_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-49_50_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-51_52_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-49_50_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-51_52_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-49_50_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-51_52_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-50_50_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-50_50_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 45d2d70cfb..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-50_50_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:482aa8ffda0649841f0247e706aeb133b9786fcfba94f66696238f261c26c27f -size 21050 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-50_50_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-50_50_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 53f02e6e24..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-50_50_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9ea317ebaa7f046ac2dd379287d05e8c47eff55fde7d3416de2c68d3a198a1f3 -size 13361 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-50_50_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-50_50_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 53f02e6e24..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-50_50_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9ea317ebaa7f046ac2dd379287d05e8c47eff55fde7d3416de2c68d3a198a1f3 -size 13361 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-52_52_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-52_52_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..75568a5c24 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-52_52_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e9b9084a1bd231828e99d09cc26a215582be791cbca042302e9bf2abf9b14b2 +size 21046 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-52_52_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-52_52_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..0471c667bc --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-52_52_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f8e26aa8eefcc880ce5f63dc51f528c8237e53ff8245be3081ea5756b5902cc +size 13357 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-52_52_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-52_52_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..0471c667bc --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-52_52_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f8e26aa8eefcc880ce5f63dc51f528c8237e53ff8245be3081ea5756b5902cc +size 13357 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-50_51_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-50_51_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 12f09a4f83..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-50_51_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:49ed5149e46df6b7c7691f8c4d6ab9b03b9774d2ce1d04a46a793e7edb11eed0 -size 20162 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-50_51_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-50_51_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 868b5535a0..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-50_51_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:801ae8d88413708979b1aa9f9f19a3cedd4b81640da4bab7e2c8ed492657d929 -size 12847 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-50_51_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-50_51_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 868b5535a0..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-50_51_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:801ae8d88413708979b1aa9f9f19a3cedd4b81640da4bab7e2c8ed492657d929 -size 12847 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-52_53_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-52_53_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..25c6598288 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-52_53_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb65947f4898b59bca52895e72f7ed2d28a667980509d1ef6b1851e1779d78aa +size 20163 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-52_53_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-52_53_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..67310a0131 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-52_53_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:475d0ca23bc5ec43defcb5a87e71cc14644747c2433de1ca413c6fbd42643c88 +size 12855 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-52_53_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-52_53_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..67310a0131 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-52_53_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:475d0ca23bc5ec43defcb5a87e71cc14644747c2433de1ca413c6fbd42643c88 +size 12855 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-51_51_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-53_53_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-51_51_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-53_53_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-51_51_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-53_53_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-51_51_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-53_53_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-51_52_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-53_54_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-51_52_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-53_54_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-51_52_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-53_54_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-51_52_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-53_54_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Day-52_52_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Day-54_54_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Day-52_52_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Day-54_54_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Night-52_53_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Night-54_55_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Night-52_53_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Night-54_55_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-53_53_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-55_55_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-53_53_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-55_55_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-53_54_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-55_56_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-53_54_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-55_56_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-54_54_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-56_56_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-54_54_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-56_56_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-54_55_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-56_57_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-54_55_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-56_57_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineEventTimestampView_null_TimelineEventTimestampView-Day-15_15_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineEventTimestampView_null_TimelineEventTimestampView-Day-15_15_null_1,NEXUS_5,1.0,en].png index a004828d70..c256223bff 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineEventTimestampView_null_TimelineEventTimestampView-Day-15_15_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineEventTimestampView_null_TimelineEventTimestampView-Day-15_15_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8dc8b6abeec47a4d3287e02943a63fc4809cae93fb590cae279e3ef64ac7cc4b -size 5934 +oid sha256:d81fddceac96d72b02ec94487b724d738ca298bb479758a8d887138cbeeeff4c +size 5940 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineEventTimestampView_null_TimelineEventTimestampView-Day-15_15_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineEventTimestampView_null_TimelineEventTimestampView-Day-15_15_null_3,NEXUS_5,1.0,en].png index 1e2f0697ee..7e9424f07b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineEventTimestampView_null_TimelineEventTimestampView-Day-15_15_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineEventTimestampView_null_TimelineEventTimestampView-Day-15_15_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e6c7c6768a5f01584eceebd471a8b016bbca0bfc48be6edbe4756a97c6f451e9 -size 7314 +oid sha256:aeb1f8d6573c61dd5d9ff49a760c20928a8a5d8d52b9efd1257fe5327a3efa56 +size 7330 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineEventTimestampView_null_TimelineEventTimestampView-Night-15_16_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineEventTimestampView_null_TimelineEventTimestampView-Night-15_16_null_1,NEXUS_5,1.0,en].png index 3a120ea542..4c38229d44 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineEventTimestampView_null_TimelineEventTimestampView-Night-15_16_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineEventTimestampView_null_TimelineEventTimestampView-Night-15_16_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e83528181f62f9f86df1c7042ee013c8c61012cbf78d369b3b893b789561efd1 -size 5828 +oid sha256:2a63b3f1a04930d13a42fd1e70a2a659acad8215c72a4385855150a1d64e6ba7 +size 5831 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineEventTimestampView_null_TimelineEventTimestampView-Night-15_16_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineEventTimestampView_null_TimelineEventTimestampView-Night-15_16_null_3,NEXUS_5,1.0,en].png index e870e65204..8e1ec8bdb6 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineEventTimestampView_null_TimelineEventTimestampView-Night-15_16_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineEventTimestampView_null_TimelineEventTimestampView-Night-15_16_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2df2b9e0028e9513b60c9309f14dc1973d10a424c3f4ccba79210a0226322dcc -size 7063 +oid sha256:0ca82bc4a0165eecf00fb3e8d926bb1edbc525c7f218a4762da6105a7f16af8a +size 7067 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_null_TimelineItemEventRowForDirectRoom-Day-17_17_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_null_TimelineItemEventRowForDirectRoom-Day-17_17_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..28d5541975 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_null_TimelineItemEventRowForDirectRoom-Day-17_17_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bee7c8c0c233a2ef943e8804c77597f70672c2fe23f697295889ce06f8ba7a07 +size 139476 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_null_TimelineItemEventRowForDirectRoom-Night-17_18_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_null_TimelineItemEventRowForDirectRoom-Night-17_18_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7f791c9f01 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_null_TimelineItemEventRowForDirectRoom-Night-17_18_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b780f7b6ade56b031f25829bbe313c1e71d53f3554ebdee61faa46b2f92c218f +size 135788 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowLongSenderName_null_TimelineItemEventRowLongSenderName_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowLongSenderName_null_TimelineItemEventRowLongSenderName_0_null,NEXUS_5,1.0,en].png index 0134e6c367..120f3d26e1 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowLongSenderName_null_TimelineItemEventRowLongSenderName_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowLongSenderName_null_TimelineItemEventRowLongSenderName_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:52e88cae16af0e46208511678717c44c1d9fc91e6c6e676dc68247e3372eec64 -size 18255 +oid sha256:c691a70d0111d0b395e76e7ba94b382ffd470ea066e229bf182f8fbf631c899b +size 18242 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Day-18_18_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Day-18_18_null_0,NEXUS_5,1.0,en].png index 31b384c132..0c606c1fbb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Day-18_18_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Day-18_18_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:343d84dc714bb672084362155362b04936ae491f437e492332bff553d7776078 -size 62478 +oid sha256:851fc21681b724affa557d219b8e7be86b6058b788a5f2983f0db96f113f00eb +size 62632 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Day-18_18_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Day-18_18_null_1,NEXUS_5,1.0,en].png index 71d9675e92..b4d9d57365 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Day-18_18_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Day-18_18_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:861e5248516afd049fb0ce58e810881abe3be2c176405e6bfb65f834dd7f3b2a -size 65169 +oid sha256:d9a54f34c27eb88d05eeb6305e73d5e6b0d1c347601a2d3a3426520c2ab1703e +size 65272 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Day-18_18_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Day-18_18_null_2,NEXUS_5,1.0,en].png index 7bf4f6f57b..792ad04913 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Day-18_18_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Day-18_18_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0cb7b8bd23784f2323ab61baad1a910b343fc498ea61f7364edeff9111992d0 -size 69478 +oid sha256:bd2f473ac5685ea4dfbb565204e3d3f1b608f0b44a06c382f562b97846b1d75e +size 69546 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Day-18_18_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Day-18_18_null_3,NEXUS_5,1.0,en].png index 74c7c5080b..e2b256e651 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Day-18_18_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Day-18_18_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1d1f93a483d72bccac7befaba22bcf8c977c5a9cea1ffa157d653b229ec061f6 -size 72224 +oid sha256:bb97680e0e8a74be5369ce8d443f4dbde8c8db5b3b5c62c8f91795e913f70497 +size 72429 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Night-18_19_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Night-18_19_null_0,NEXUS_5,1.0,en].png index 57858faf5e..b2d68c52b1 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Night-18_19_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Night-18_19_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca29ff2138ffe5ad3eea9a6168d91ad97e1688787c25d761cbfebd5ea7adfd70 -size 63077 +oid sha256:4c9cb9e39505d30bd562f26f61f30f68b037b1c3ac542e8499f7f602687d508a +size 62930 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Night-18_19_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Night-18_19_null_1,NEXUS_5,1.0,en].png index 1a138a2494..0dfd3b1b29 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Night-18_19_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Night-18_19_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9eef20e4d04bb5ffc6c2bf39930866f7f3096c69798ab1312aad81ab2d5ef329 -size 65163 +oid sha256:2f26f5518b23013e254efea64ca3318cf390fe9af407b4ca29428df2dae15058 +size 64959 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Night-18_19_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Night-18_19_null_2,NEXUS_5,1.0,en].png index ae039a81ef..4b47ba4b72 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Night-18_19_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Night-18_19_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:73ffe386309cf9b49af86e77c8989bcf06e3e3e0fadc56cec060e03049ddfecd -size 69636 +oid sha256:bf37662355ec12d52d2a8a88ef58fd9743b7fe278050d6d0a5504983c8844031 +size 69625 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Night-18_19_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Night-18_19_null_3,NEXUS_5,1.0,en].png index 5608e8d7b1..5ac9ff019b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Night-18_19_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowTimestamp_null_TimelineItemEventRowTimestamp-Night-18_19_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c074e3330ab178107fb8e4d51b9e09cf60b9b33b483deaa7104cb687942ecda -size 71593 +oid sha256:b5be47550b5fcd0ffe3def6a4448e47c96641224100ed82acb977ee033527714 +size 71424 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_null_TimelineItemEventRowWithManyReactions-Day-19_19_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_null_TimelineItemEventRowWithManyReactions-Day-19_19_null,NEXUS_5,1.0,en].png index 37614c5021..4a664e9a92 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_null_TimelineItemEventRowWithManyReactions-Day-19_19_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_null_TimelineItemEventRowWithManyReactions-Day-19_19_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:31e1b67d218d8f394c8d14b66c1964a12ff7b7ffbac79f353342f3342f867287 -size 84807 +oid sha256:f9c4c522daf904898d38b32196dc43628697816381c210ba404321df0b409f2c +size 85265 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_null_TimelineItemEventRowWithManyReactions-Night-19_20_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_null_TimelineItemEventRowWithManyReactions-Night-19_20_null,NEXUS_5,1.0,en].png index 05fa0e511c..183b28c2d1 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_null_TimelineItemEventRowWithManyReactions-Night-19_20_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_null_TimelineItemEventRowWithManyReactions-Night-19_20_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9bb2095dd487e0371c5c876d2d86cccdc77e883df4a06ed1444c93e27e7b0044 -size 81320 +oid sha256:9717496e144f7a27919723d037d61668289b5349f2885bd5fc1489606c726625 +size 81546 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithRR_null_TimelineItemEventRowWithRR-Day-20_20_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithRR_null_TimelineItemEventRowWithRR-Day-20_20_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..57b1bc23df --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithRR_null_TimelineItemEventRowWithRR-Day-20_20_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f7f88ed6f4c84ebe5c9dd6c46474fac299b2b2e8edce0bfebe49064bcaab5fe7 +size 27226 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithRR_null_TimelineItemEventRowWithRR-Day-20_20_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithRR_null_TimelineItemEventRowWithRR-Day-20_20_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..36f5b63929 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithRR_null_TimelineItemEventRowWithRR-Day-20_20_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd8ab01406157ba78a9923611a9d21d5afe6d1d433f3aa3cf263b1507fd7eee7 +size 26774 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithRR_null_TimelineItemEventRowWithRR-Day-20_20_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithRR_null_TimelineItemEventRowWithRR-Day-20_20_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d6dd56d06b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithRR_null_TimelineItemEventRowWithRR-Day-20_20_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e7aafa97412bf18c9820aa158d0e1c2917cc471fa1977bf2ef6cc68d3a5bbc42 +size 31933 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithRR_null_TimelineItemEventRowWithRR-Night-20_21_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithRR_null_TimelineItemEventRowWithRR-Night-20_21_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2f508265ea --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithRR_null_TimelineItemEventRowWithRR-Night-20_21_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a162bad1922fb62ab2da4fe396fed4192800e21b9bccff1c5b309a8cd482994a +size 26169 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithRR_null_TimelineItemEventRowWithRR-Night-20_21_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithRR_null_TimelineItemEventRowWithRR-Night-20_21_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c7949415f4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithRR_null_TimelineItemEventRowWithRR-Night-20_21_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8198c156b3dcb61583d2543b3d2f4e62adbd54b9ac8579b9c2fbd9a8c6f8bc41 +size 25604 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithRR_null_TimelineItemEventRowWithRR-Night-20_21_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithRR_null_TimelineItemEventRowWithRR-Night-20_21_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f15612969a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithRR_null_TimelineItemEventRowWithRR-Night-20_21_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4beebf86fc936884447204fa81ef2104e25d60bde1581d077468d2ed6f3cc1ad +size 31052 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-17_17_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-17_17_null,NEXUS_5,1.0,en].png deleted file mode 100644 index dd2855cf76..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-17_17_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:55eb9c74142b1e88d0ffa51f58bcda084acecae98a94075479fc080d60c432ed -size 133105 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..62e07660fa --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7db830480ca028fc17f731b2f5e507b43697f908809ef496461b6974bdfb2706 +size 167384 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4ef99c01e9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d63fd4fe914328f70880b3157f13bd48e583c5d57edc27dcdede82044514f699 +size 171789 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_10,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..497362a4c3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_10,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:00c5efab417077f1a6c0dfdbd6f63b0faa5e9ee3174f0b22aab1dc632330e6bb +size 165109 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..61a86a4db4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:46a9fc41736c83b40c697beed7cbf3c14b3eeeb889d229e77d85cf68b93ac67e +size 154122 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7cde336299 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e7b3b360caf897eaf402091d6ec0c9ae28683e6c94d9e9bdc44c2a5c06b5da04 +size 152475 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..1ae730d2ab --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:07843d212b755e8e4a19d270b06d3afcda0e5382f5ccc098c19e5268fba585c6 +size 161056 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..31d971f33b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:28844b117cda36b3afdd7c791f52949d7a0095863d6c606090fa3e200fc6a125 +size 151427 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..041a302f61 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34da0e10f040d85752c06cb9af84ef02a3f93a64dfd56d4fcd9e10423e0729d5 +size 154430 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b7e28156f4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ab9006744fade5db0e20d5d5add106125a78a22c37c5643bb2c14a95923d901 +size 162320 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f0acb05e6e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c6d9f8e62850eb1bd7cf1bf89436b18b70e976ef3c6a3c33d880d6ac2eccac29 +size 151678 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_9,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b36e6e0270 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Day-21_21_null_9,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36b8b5a7dd154dc69732320c35c8e0a8a77fdc03365b0ea311d03cdf3c7f71cc +size 150907 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-17_18_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-17_18_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 3d11659512..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-17_18_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:46611834ba7748c328a5e51b3ed939af8f208306926e1f1656f052ad1cdc63d6 -size 128083 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..9dfd9eb86d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:99da12740d48a70bc54fb07ac4c765dd442d94883502fc1b6c07ef345ad56ad2 +size 165103 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..811eba2d4a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a59b0f1fb3a46bedf01520ec1167dd1d33d73d6da5a9b9ad690f13eaff11608d +size 169346 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_10,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a5e691043b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_10,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0937e4b1616c76b40ef2699577ce765ba7c01f5c22f12677b5c161a4899b2021 +size 163218 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f56ef28131 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f41aeb0c842979624d2453e8c95974a2ed3971115f5ef56e6b90841d94e1a7e8 +size 152790 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..75020926a5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0bfc14e7432e00bae59d44362034398eaaa5676bf591128870deebf622758eb +size 151065 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a4e36c2c9a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3fdb4183f704b8f1cda0712f2389f3b46e9e47a7ff59f954d2cc494928ecb381 +size 159382 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c1b187f0aa --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:41c59480defc44111dba7e9fb3ee020f55f20265cd24ee6045a52438ade062a4 +size 149840 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f8de679b64 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9071c32211b55d05947071631d2da06bb1b17f87fa087b478f92ca9faedda3c0 +size 153189 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..0d853e581d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6e650e9054ed49ad539718843b36d6065954388cec1e41e362de5868d471398 +size 160496 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b96562422a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2cc8fb2604f10a423b39c75c0824632197b52304e06b5a33f3d8300ba3390327 +size 150050 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_9,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..9d17bf1483 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRowWithReply_null_TimelineItemEventRowWithReply-Night-21_22_null_9,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ccbf09b9dd72f8bfd9ed0a9ea7af3990d513452614dc04ff13b0389ec097a2e5 +size 149329 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRow_null_TimelineItemEventRow-Day-16_16_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRow_null_TimelineItemEventRow-Day-16_16_null,NEXUS_5,1.0,en].png index e55f448d13..f444bc217c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRow_null_TimelineItemEventRow-Day-16_16_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRow_null_TimelineItemEventRow-Day-16_16_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8598a191c36789a925c1fdff3a6c9faef19d2d41896a2b47533f7ceed9fb60ba -size 156693 +oid sha256:c4b726ff874141615824c929c22518245cd9ddcee331a2d1b1e953ce98940627 +size 190995 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRow_null_TimelineItemEventRow-Night-16_17_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRow_null_TimelineItemEventRow-Night-16_17_null,NEXUS_5,1.0,en].png index e005a309d5..7a2e26406f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRow_null_TimelineItemEventRow-Night-16_17_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventRow_null_TimelineItemEventRow-Night-16_17_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d738bc8aef1846dc2bae8bd669abbb40dbc9cad28e8b2581fab592930d21f8ff -size 151630 +oid sha256:b3a8db4dab06f907fd3a83de9c9dd81a9c6671a6e7e77d4af78e1ef1795336db +size 187116 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventTimestampBelow_null_TimelineItemEventTimestampBelow_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventTimestampBelow_null_TimelineItemEventTimestampBelow_0_null,NEXUS_5,1.0,en].png index 92eba5f9cd..bc50eb63f4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventTimestampBelow_null_TimelineItemEventTimestampBelow_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemEventTimestampBelow_null_TimelineItemEventTimestampBelow_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:53d9982e5a8bfd18e007bd4dc80469367f6946b993fcc5faeb8a28ae2343e0b2 -size 56596 +oid sha256:d261ba035401e1624a48911f1f9072a07e134e5d3b870fe488e8bfdb681c092c +size 56591 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_null_TimelineItemGroupedEventsRowContentCollapse-Day-23_23_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_null_TimelineItemGroupedEventsRowContentCollapse-Day-23_23_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..49a9557b79 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_null_TimelineItemGroupedEventsRowContentCollapse-Day-23_23_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61760159b2e8967b5349392935e88fd62353d1f8b8842b4e02090a20b34d7956 +size 8896 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_null_TimelineItemGroupedEventsRowContentCollapse-Night-23_24_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_null_TimelineItemGroupedEventsRowContentCollapse-Night-23_24_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..af82f3930d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_null_TimelineItemGroupedEventsRowContentCollapse-Night-23_24_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:65d4b7b84353c27830048ff783e1f1cf99e81b23bac46152ec17c16dcb63546c +size 8994 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_null_TimelineItemGroupedEventsRowContentExpanded-Day-22_22_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_null_TimelineItemGroupedEventsRowContentExpanded-Day-22_22_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a61015babf --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_null_TimelineItemGroupedEventsRowContentExpanded-Day-22_22_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:16fb79b80d6dc1cd032bdc4383be80d6ed0f4eddf4bf42e95e28cc5c92635154 +size 14560 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_null_TimelineItemGroupedEventsRowContentExpanded-Night-22_23_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_null_TimelineItemGroupedEventsRowContentExpanded-Night-22_23_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..588014a61b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_null_TimelineItemGroupedEventsRowContentExpanded-Night-22_23_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f067cd464e7c14b0034fcfdbe4df3dba2bc95c05b8ac48f6788395f4631dd075 +size 14601 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsLayout_null_TimelineItemReactionsLayout-Day-20_20_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsLayout_null_TimelineItemReactionsLayout-Day-24_24_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsLayout_null_TimelineItemReactionsLayout-Day-20_20_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsLayout_null_TimelineItemReactionsLayout-Day-24_24_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsLayout_null_TimelineItemReactionsLayout-Night-20_21_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsLayout_null_TimelineItemReactionsLayout-Night-24_25_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsLayout_null_TimelineItemReactionsLayout-Night-20_21_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsLayout_null_TimelineItemReactionsLayout-Night-24_25_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewFew_null_TimelineItemReactionsViewFew-Day-22_22_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewFew_null_TimelineItemReactionsViewFew-Day-26_26_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewFew_null_TimelineItemReactionsViewFew-Day-22_22_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewFew_null_TimelineItemReactionsViewFew-Day-26_26_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewFew_null_TimelineItemReactionsViewFew-Night-22_23_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewFew_null_TimelineItemReactionsViewFew-Night-26_27_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewFew_null_TimelineItemReactionsViewFew-Night-22_23_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewFew_null_TimelineItemReactionsViewFew-Night-26_27_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_null_TimelineItemReactionsViewIncoming-Day-23_23_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_null_TimelineItemReactionsViewIncoming-Day-27_27_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_null_TimelineItemReactionsViewIncoming-Day-23_23_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_null_TimelineItemReactionsViewIncoming-Day-27_27_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_null_TimelineItemReactionsViewIncoming-Night-23_24_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_null_TimelineItemReactionsViewIncoming-Night-27_28_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_null_TimelineItemReactionsViewIncoming-Night-23_24_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_null_TimelineItemReactionsViewIncoming-Night-27_28_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_null_TimelineItemReactionsViewOutgoing-Day-24_24_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_null_TimelineItemReactionsViewOutgoing-Day-28_28_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_null_TimelineItemReactionsViewOutgoing-Day-24_24_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_null_TimelineItemReactionsViewOutgoing-Day-28_28_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_null_TimelineItemReactionsViewOutgoing-Night-24_25_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_null_TimelineItemReactionsViewOutgoing-Night-28_29_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_null_TimelineItemReactionsViewOutgoing-Night-24_25_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_null_TimelineItemReactionsViewOutgoing-Night-28_29_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-21_21_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-25_25_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-21_21_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-25_25_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-21_22_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-25_26_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-21_22_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-25_26_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemStateEventRow_null_TimelineItemStateEventRow-Day-25_25_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemStateEventRow_null_TimelineItemStateEventRow-Day-25_25_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 7c82564876..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemStateEventRow_null_TimelineItemStateEventRow-Day-25_25_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:176c3dd24f1864e2690f18c8e0ff5a37f946edb8334d3aceb24593c5409aa598 -size 6968 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemStateEventRow_null_TimelineItemStateEventRow-Day-29_29_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemStateEventRow_null_TimelineItemStateEventRow-Day-29_29_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2ff538ae01 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemStateEventRow_null_TimelineItemStateEventRow-Day-29_29_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bccbd7510d41c9a4645942669793ce9a8f908d54580bfb56b846258784306e06 +size 7625 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemStateEventRow_null_TimelineItemStateEventRow-Night-25_26_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemStateEventRow_null_TimelineItemStateEventRow-Night-25_26_null,NEXUS_5,1.0,en].png deleted file mode 100644 index b8fe62070f..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemStateEventRow_null_TimelineItemStateEventRow-Night-25_26_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6d52781cff2df18c5ba052551abb0ab72bc4407e6617f43362e484df2d9d9e2e -size 6863 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemStateEventRow_null_TimelineItemStateEventRow-Night-29_30_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemStateEventRow_null_TimelineItemStateEventRow-Night-29_30_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8f1b702891 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_TimelineItemStateEventRow_null_TimelineItemStateEventRow-Night-29_30_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:269627098efeb12c65ccf8c4808d590815b6eb2368a6c0986a541f8f50f22405 +size 7659 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-55_55_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-55_55_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 84ff0e89da..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-55_55_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7037bab35eb36d248b7ed2605a67c003c9d8f74e315d2b5c9097415c50c0be2d -size 34912 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-57_57_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-57_57_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..01a75fc6eb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-57_57_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36b849bac96f0f45bcc55315a191951260a19d85573f7b85c538c82e371413c1 +size 34907 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-55_56_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-55_56_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 87c41b8968..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-55_56_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:da19273bbf3dfe1bad06e63ab05b30a58885386f7c2017912ed15f3a1856fdba -size 32843 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-57_58_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-57_58_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..abfb154bec --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-57_58_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93313710595fb98a906d6783330d1b91ebef8a905a68c52450b8ee0fae422595 +size 32840 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_0,NEXUS_5,1.0,en].png index caa0e940f9..50a5bd575a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15f1315ee78f9285ba33b6c0f3601b409a6965585446cdac566b65a2e6b55e57 -size 52374 +oid sha256:94be80f7b3f2a4fb3cc7f89c5e013e1eb99154862a46420ab6d30cd18fb6587d +size 53018 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_1,NEXUS_5,1.0,en].png index 96deb94eef..4b52d6dc00 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0fa1ad2fe7085badb03368def655e9f175c497e91eb1de440df8fb64b2fe858 -size 74398 +oid sha256:47bc7cc50be3b70e1441debf5e140fd637b457e237d09b421e0fc3a7a8181da2 +size 74753 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_10,NEXUS_5,1.0,en].png index 039022ff12..15ddbd88c6 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee02c20c1ab0b514e12461c95428ae89985ae86fab9e8538eb2301eeaa910603 -size 347767 +oid sha256:5d434fca6c31c1048f9e9c1beccb70e13ed444a3fd08d82202774183f3574d25 +size 347891 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_11,NEXUS_5,1.0,en].png index 3030f83068..9693e36f12 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1fc3c477b1ce2c74b32cce19bb94c61f49bcf01f751b136c0ff75d573bc66a35 -size 80231 +oid sha256:254cef5f8a455f62f334fa396b86b81d46459eb9b0e54355cb051214cc76b70e +size 80188 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_12,NEXUS_5,1.0,en].png index 7defda84d6..681a409209 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_12,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_12,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7a121baf52bf6997fa6dcfeee9ce77e6d0f3b374227addae4a28305551a6914f -size 53893 +oid sha256:cc02bbef3944a10d19d926107ae27e1ce5abb82aa8532ff3f49e16d4239ef638 +size 54649 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_13,NEXUS_5,1.0,en].png index e1cc82e2af..9dab8c15c7 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_13,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_13,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c433475dd2eb980e95ef67bf04c2465db1449bf34b3c0eca7f8c6f43b35113e -size 66532 +oid sha256:68bbb20edcec05fc3675ef998a90058c5cb9ea831656676c7f316be4c7ce03a6 +size 66909 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_14,NEXUS_5,1.0,en].png index 0f72363fc0..4584d2f98f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_14,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_14,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f531805df6b1b7541e1c4d443979cb5434d3ea6c688725debc7f8845abebccf -size 50375 +oid sha256:3f9b93c292973432b32c53e33a621e7d7626fe838653550738a6a9dd3925cc82 +size 50687 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_15,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_15,NEXUS_5,1.0,en].png index 024e61ae28..9423f23085 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_15,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_15,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d98aac1065f283b232c96f5a0032865e81bca8816bd235615e131d4bb104a9a5 -size 67474 +oid sha256:f7b1f9f6f91e2b4824270af423cfa637822462c21b2aa1660d61a64821084d28 +size 67754 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_16,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_16,NEXUS_5,1.0,en].png index 2eb5e3ea2f..791f741b0b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_16,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_16,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b06025fe43c1cd3f39bcee49222952f3750d0cf7ab2b9281716fcc670ea6934 -size 57499 +oid sha256:f8c841112673d692b508315a9b73132ec38abe9a2b3f0aea43859d5cebcdb970 +size 57976 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_2,NEXUS_5,1.0,en].png index a51b6701aa..15e23a7d03 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7397940f42e0d5c5b860fcb0f08519fab590eb155efccaea84e22c499c9b9a5b -size 230534 +oid sha256:cf97bbee3772485bb2abf707bccc29693c478a5f723dfb3bbf9f35aa9d3e0cc2 +size 208907 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_3,NEXUS_5,1.0,en].png index f5d969f29a..5f759a4d32 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:effff5164fe2fac13a9387d2bcab801c3d27cb77a7150cb0a062f8d82f4299f8 -size 231415 +oid sha256:f25388c4d203cee75790b473bcb46c49ea5bdf97f10b8a11a278e889dfb2fc49 +size 209768 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_4,NEXUS_5,1.0,en].png index 54c2dc5b1b..8f303114c3 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5cdd36c1ce3517f3a8c90fc4081e59c798f31c77bee3507d99d334da91878b0c -size 72111 +oid sha256:8b0304f8178f2c1812f4ba9495597f2671a19c8c06864269557458e5b952a89c +size 72699 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_5,NEXUS_5,1.0,en].png index c43efb8476..d42f3117b6 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8592f1320fdae003723d7f2bfc0f0e8dadf3b8edd1b5605dd0771e9b21bc05f7 -size 87604 +oid sha256:bc9394387d75232efad1f6a571c40d1fba5f5d5de1c85a0f004c4a52e8ef2428 +size 88489 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_6,NEXUS_5,1.0,en].png index f0f3f5e927..ef20a18aab 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eea033f081eb0b62671b3b9c5a626a4d1bfb0b386ff0a8e0011effc40858add3 -size 74258 +oid sha256:e6d3d23219a9cae2a751e4f279fd2537bb6d90147d010bb60ecd0eabccf93af9 +size 74378 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_7,NEXUS_5,1.0,en].png index a5c3e2429a..bbd87958ec 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc7461871275f11e5ee7abbb6dafaecf7b2c6eac38b3a538ac80b6b722aa0e16 -size 105810 +oid sha256:e2723ba9980e32cc50b2850f2656ef4cc206328cd53dfeb98618eae3fced13a1 +size 106852 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_8,NEXUS_5,1.0,en].png index 374f6400ae..53c399f2a6 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4dd52ecde13c76639225b482238924485b8cdbbc56f1b270254689f06cae7e8 -size 58016 +oid sha256:aaf8182f15a45208c2559cc40225d3c48bb3517446f4a3521864fbac2a2b5458 +size 57621 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_9,NEXUS_5,1.0,en].png index 98e3ba6dbe..8ba69aa893 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f14b44ab65ac39c868de2c28644f886923fb4058f29a0d5b12dca7b6d978238 -size 393377 +oid sha256:c6bf57830c8e8766e8209eec18cd245fb3a49f2698dfffb60ec5475c95fc4bea +size 393334 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_0,NEXUS_5,1.0,en].png index 7c5a9e6200..836592bd65 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4fea4f3bdf3730d26880aa44f32bbcb5a41f5dfbe760d893852ded057f2aafeb -size 50581 +oid sha256:77c5f301bdff94528b1153318838a84bb85032cad0bc2ecfdd90fe0e7fb869bc +size 51266 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_1,NEXUS_5,1.0,en].png index 529f9ad95f..1e3b03dccf 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5537a6de5da545df178a198a402dd5fe67f08ff64f448c3ecd6245484766cc1 -size 71354 +oid sha256:b41f17e7a2b5a167defabf689ec260f0efe44307d2a422f8c543897f7fea14c8 +size 71634 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_10,NEXUS_5,1.0,en].png index 99404cf6b2..0ce3acec5c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1b1164272dc31a867c7dbc2bae37ac960168f31da63b233531d509583ea0cea2 -size 178173 +oid sha256:9c6e037d1085366ca0212bdb7570c6c5d1c161e92d8929257699c12f3329846c +size 178233 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_11,NEXUS_5,1.0,en].png index f056537e1b..d356dfec54 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eafc87ff92c2d9b1e50bb97251a1bba132afdd6f6ec6769146c3e322ecd95da6 -size 79207 +oid sha256:9d22db0588d9a6a355246cad3f94fa3a425b80b2624081e976977990c331b3a6 +size 79274 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_12,NEXUS_5,1.0,en].png index 55f9987532..f8a5078c2c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_12,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_12,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd5f7838b304537265aa2616fd3b96db72346e963a785aa683826870e1a0190a -size 51920 +oid sha256:2193a11873fdf07b1d6fe990154c8506153b0beef7d971dcefe78d1e1b63a0a7 +size 52888 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_13,NEXUS_5,1.0,en].png index 01ed22d8b0..754e549690 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_13,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_13,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9157f4531a208f2605bfc93314a669d188904b43b2f29bfd154786a50607d8db -size 63928 +oid sha256:54d20d4100b8ba70d76c236abd0be7ccf9dfb2bd432fe552b8a18bfce1ef5b84 +size 64244 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_14,NEXUS_5,1.0,en].png index e8d97cbf89..02f1c64ba4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_14,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_14,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:93482a9053b5efc9331457b907964409e85855e4988358df6a01f1bea7ff1b9f -size 48797 +oid sha256:1680c3164c780dafd46462d6770a6a84c29ea46e5a7adb6d834164cbc2bd14b1 +size 48945 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_15,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_15,NEXUS_5,1.0,en].png index 462776ac31..01c63b0509 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_15,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_15,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:70e0a380cca7ba52a3b695b82c31e68c82c1e03ad6364e6ba73e84f070295528 -size 64748 +oid sha256:da410a09335165b25ee3ce4fe6b7f03a5bc67dc546a85ab45c06ce78a7adf56c +size 65011 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_16,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_16,NEXUS_5,1.0,en].png index c54f9ea050..f660b35a10 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_16,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_16,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bab28935e51e099811884a707858e5f463bb591a8a2766a276044c0dbbae92a1 -size 55244 +oid sha256:c94aab9ca99a3cc6b04ce1041251c2f666619728c6421c3dd12e691cfb3bb365 +size 55787 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_2,NEXUS_5,1.0,en].png index 04337c2fd3..b00b2a56b5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a7783a8fbf96f9029ccac649f43b05c2dab7bde9372e92669c27f1097cb1cb0 -size 229846 +oid sha256:8a5fa5cda7c17fd99d1e8ab27c37bc9e53ef9ef42288f140ee438d03bdda45de +size 208480 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_3,NEXUS_5,1.0,en].png index f4137243e1..b1b5c57204 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe254fe851bbf538defe1a7e3d62fa9584a24fc94f9c3902f1b7d0258726a615 -size 230751 +oid sha256:55392b7f740c5074750fb4b807f1e1e4ca6dea9b2601742596251bd52bd098d1 +size 209337 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_4,NEXUS_5,1.0,en].png index 7c4d6b85b5..7891502c89 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ab7bd9d8d2ea21c3034010f521b92b0637d430b231a75bc3d5b241f10d172c4d -size 69620 +oid sha256:bae3ea763fdf8d118909f2c22d4eafda5ad4ceda3b24c846002e10e6032c196e +size 69826 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_5,NEXUS_5,1.0,en].png index d7db8f7395..6cc4d25d99 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:39bcfcd74eff6df911cb6401e4bcd65d3bf1c6d942c0db730072e762e0afb3c8 -size 82864 +oid sha256:a1b12ee386b4cd9601fae1b1c4ed1779defd7a10ca929bb5cd683f7039cf9fc1 +size 83922 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_6,NEXUS_5,1.0,en].png index ac8e6cf4b4..b81b3e7599 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c475d64ef991b3438afd84d8b93d40eedc8060ed862e64523efc0458e6a2a477 -size 71450 +oid sha256:12642b59eb842993428ae31724cf885647e243eb36e37778350879bac9571b0e +size 72187 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_7,NEXUS_5,1.0,en].png index 7aebcca885..b30b840c3a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b1eaf05f8e22a9b22f75d6dd28c1c9c004918f6e85cd60ee0ae245e43affccef -size 99788 +oid sha256:7200a3147b8b0dab653bf225e8b3e1d93963b79852d02d3948194d50ffafb6ab +size 100870 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_8,NEXUS_5,1.0,en].png index c608f99a68..f57d2fb933 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be817a2061d43e210c105b193f1baed6060c51f11d97f45d0593cb40ab7acd98 -size 56403 +oid sha256:09280f50fb09e766b03aef83f267f91f24688b435321de29f797fa10a4e3fe72 +size 56093 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_9,NEXUS_5,1.0,en].png index 0ba45d341a..50d7c9607a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5cf70a69d194c8d297e03ee75be7659d06515c9698751181bdfc8f9da7da6055 -size 189520 +oid sha256:45c8a459dc025a00345b34b41fb3051722f9fd6875ec77afe301c653ee905fd8 +size 189462 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_0,NEXUS_5,1.0,en].png index 4b4c8a1c3c..c291649ec9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0eee7139a7675a08899accf49a97139b56333737e649e51ca1644ae6ae68d915 -size 54358 +oid sha256:d0752f68d9ab404f2669f631df280a4f48bcec3565b85f9876cd8bddf030530c +size 57041 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_1,NEXUS_5,1.0,en].png index 049ce0c7a1..0376a8a8c4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:39e0b212a2cc423f3e33254d4a965c3071e88e5dd21268c42b3b555b1800b236 -size 55774 +oid sha256:ceeaad4f95c818f1e16995e7d5bd4e1cc5ea82e7a6a7560d88b30cce33b779de +size 55971 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_10,NEXUS_5,1.0,en].png index 6f2b4996d3..e9dd36bd4b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:33afbe67d0e4156d615e6a58d4356386d5e2c0922cf646c2ec385338d869d8f5 -size 56023 +oid sha256:1a9cfb621dd9ae156f53d9f8ec0a2b06e9fb5c6955139494e42badd2b0e9d224 +size 58717 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_11,NEXUS_5,1.0,en].png index 2251ff798a..d2a3d6aad9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:083476cb480c6641924331689121581300247fd9d1f61c155a35389a07601c69 -size 51237 +oid sha256:2472128fa67e6b12600779b438fd04c4a54a300b9b67231fab8bd70dc5c9e399 +size 52787 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_12,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..cee3274b7d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_12,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:66349c80a6788cebaf782bbf47d05612f0bf18bac1ce4ae8b4cf0feaf682aa0f +size 57056 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_3,NEXUS_5,1.0,en].png index 3db6eef5cb..ee9a0d2b06 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:42445e54ca2cfadbbd3f041f327825ea5595e27877376a760c37b746529d0980 -size 56135 +oid sha256:e0264c1fe5cc506dc79177056fa0d3ffc13d772ecbfda0dd78a4c15283bc98a6 +size 58937 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_4,NEXUS_5,1.0,en].png index 143870a1b8..a97e712c2e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b9f1300da0ac7ef29286fb37d4489b38fa07632f71aa7d1f1cc0e0786749c626 -size 56124 +oid sha256:5c4a0a5eff3cdafd895069be1eba53929d9ce90e2b6e3f0766ce14d85e38544a +size 57545 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_5,NEXUS_5,1.0,en].png index 6c6e0a9ef4..71e8578bcd 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95f81914a56679419a7a6055040ae51b11616c87012310cbd221e3af41d55e8e -size 52039 +oid sha256:2f146264ed00c4537970fc3e8722de8a0e378faed86b4436b8c666a70b9d08b6 +size 54838 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_6,NEXUS_5,1.0,en].png index 8d15129b08..8c75a45b99 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce97ccc29b01824ddd251dbe7756aa80749c0acc4fd8278cb1c3e4fc24c2337e -size 52334 +oid sha256:8f734a28b795e80593b5aa661468c190d816d3f91e47cdc7a0e59a0b5d2f3b7d +size 54685 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_7,NEXUS_5,1.0,en].png index bfa886fbed..f4770a9254 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d387f9678367ec0b8d6617a3ba378d309979af0e534e9f683805e71cd6c2794 -size 60172 +oid sha256:d635a43a26b042918890f33d3b520a17dd66a26c56816db35104690e39e00241 +size 61803 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_8,NEXUS_5,1.0,en].png index 26057e68a3..67c2fbbcd9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0708776dd8884bd8975845c9fbb0b30fe6317d5692aaba36781fed5cff065c1b -size 42279 +oid sha256:42b443f7e333c3c9aa21bca80e6423e324e9371b3d95057691b8ac6a4afa7a89 +size 42919 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_9,NEXUS_5,1.0,en].png index 9b5fc514e3..4d99bce2cf 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:766086d64f70b2a9f028149190d3f3c5b2ff34599907b38997657b6d093fa0f4 -size 41428 +oid sha256:34b376b868b36ca264f40fa2df6bf188c48b47fb0721d40a3723721c7c7006b3 +size 42079 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_0,NEXUS_5,1.0,en].png index 6ae5a8b8e0..a1e95f5cd1 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d42806b454f15ed83e883bf5e2765d345163a57049b4b598c1b8d08458588dc -size 52624 +oid sha256:b1d8a3d517a76f8f31797cea326f910498fb01d58936099a8ac108f05047d5c1 +size 55090 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_1,NEXUS_5,1.0,en].png index a432f0e416..240f3f24e9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:27f4fd957e004579b8b20373e1649f8c067a604595a5dcaae60b0caec7c43954 -size 53972 +oid sha256:15d87200255f29af6fe4b2bc96230398e651ad000eb9c787080fd2796e910a4c +size 54019 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_10,NEXUS_5,1.0,en].png index 2b1a73c36a..cf2c7ca123 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:62504045bfd1b2214e6c80d266ba1c92f750ddfff70038c55f2ea9f59e6fc3a1 -size 54127 +oid sha256:766090cf3f25c0204f545a641e9b0f2c6f286ab9bc657ec4414874627d6ee9d8 +size 56528 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_11,NEXUS_5,1.0,en].png index a4bc4d4f6b..03ed03d378 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:33b0fb2e0990a008ea6699a0d0fc59478bc3ce755637584af4e2577fc007e2c3 -size 45809 +oid sha256:1e1d7f48513465f04747e69fcf28c479e69be8423f25cae0d34698ccdc80371f +size 47408 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_12,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..df78dd8f75 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_12,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b7119057bd9d33557587abbb568ecd763da97eca129ce3379119cb1c37784de +size 55121 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_3,NEXUS_5,1.0,en].png index d5195bd919..6cdda51b60 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:397f061ceeefb2ea67a5d9a342050cea136554e936854bd26b92c3acfde45d74 -size 54646 +oid sha256:a29fe9421500a439dcaae256a52c245c4ff67433afee143b98d26eb867c7ad33 +size 57188 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_4,NEXUS_5,1.0,en].png index 5f884c0794..82ebda2cfe 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:090ea31e1183d552111c3c92c535cfbb316866a9832d8f26fa1f7bda6ce03cda -size 51042 +oid sha256:46cb86b858e96ef96c5bf6f89a77d486d34481114b16f8413a433d609ce3c58e +size 52555 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_5,NEXUS_5,1.0,en].png index ef03d74710..e5e2dabe70 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:99ded72c99dc1ba5b8c61cbacca6cc91f4b6325a16c12013150c6d63afd28182 -size 50273 +oid sha256:f7955d6751dead5e8b44b65c1cf1e5b5102aac713e6d692b94d8b548e028a0a3 +size 52730 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_6,NEXUS_5,1.0,en].png index 5006c63543..34dbef58d8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2fc04a6c21e851b4e10c88ad9485f43f2f1576f5aeb40d4611513352341536e -size 50384 +oid sha256:f29d21042d83de1c24b2e612c3a3daae68ea96906002296b08bd1af821b42660 +size 52647 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_7,NEXUS_5,1.0,en].png index 5ef67cf379..90a991e1ab 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:43476c4f4f2ab6eef1286ad6083d9ea01659ea7d96a49fb0f6ea5ac76158369d -size 54860 +oid sha256:6890d76b297c174795fcd3f36c053633592bd8f331cba8c9db89857917ee1419 +size 56552 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_8,NEXUS_5,1.0,en].png index d9af4e88ee..8a2721e721 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:459029e7871910e085378a40ff503d286b72fb7029c251d1e04a8a1f39cfd0ba -size 39017 +oid sha256:806c01a585b7aa1525638f0bf6dfccd170ff7e82b320fe08d0da2041820e3846 +size 39619 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_9,NEXUS_5,1.0,en].png index 3f51c51648..5d907d77f8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2d6cb8081da15f265d35e909c1d22bb6a48219162b20e1472230a698fdc2d2b -size 38231 +oid sha256:28cf0498aa9b15ba869a0f7b9595ab8f6c6f7a4c071db3af1b0ee336390be94f +size 38829 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.networkmonitor.api.ui_ConnectivityIndicatorView_null_ConnectivityIndicatorView-Day-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.networkmonitor.api.ui_ConnectivityIndicatorView_null_ConnectivityIndicatorView-Day-0_1_null,NEXUS_5,1.0,en].png index 5416e206b3..b10695ee03 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.networkmonitor.api.ui_ConnectivityIndicatorView_null_ConnectivityIndicatorView-Day-0_1_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.networkmonitor.api.ui_ConnectivityIndicatorView_null_ConnectivityIndicatorView-Day-0_1_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f8835edda00d6276ae8922f1d87ee0147b5caed4e6cabe52ff777bd18dfb5d9 -size 6609 +oid sha256:77be510574b4fb06a7e509dbcb8bcdd953e2841de853c1edf98bf1e57c2ee0db +size 6607 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.networkmonitor.api.ui_ConnectivityIndicatorView_null_ConnectivityIndicatorView-Night-0_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.networkmonitor.api.ui_ConnectivityIndicatorView_null_ConnectivityIndicatorView-Night-0_2_null,NEXUS_5,1.0,en].png index dbfbf2a730..8d2c74dddb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.networkmonitor.api.ui_ConnectivityIndicatorView_null_ConnectivityIndicatorView-Night-0_2_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.networkmonitor.api.ui_ConnectivityIndicatorView_null_ConnectivityIndicatorView-Night-0_2_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0c25984496c38f73e875878238a9e602f90582a31e2f5ac10163fa1ac0c0990 -size 6378 +oid sha256:14425be6101dd4ffb72e73ac41f7191ac36b13b088b392543df3f2a6bbf81533 +size 6392 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_4,NEXUS_5,1.0,en].png index a561f96b8b..5d7e8bf7d7 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b9ab4177d12e1fb15ab6b8823729cf18021dcda7013520589b8e7cf21b4bf29d -size 316946 +oid sha256:aaf9e59966650af342740484f37b7e5da334a72a96faa318db0dc9ea012d891c +size 316954 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_4,NEXUS_5,1.0,en].png index 69757c3199..d40e2236b4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:67735dd53d6dd9d86d3f31b1772c239e16c708cf890d6806fa3f6f11f02d0dfb -size 413350 +oid sha256:b19d128ca665799591b6a570be4643e482821bf9d632dc7704aacd13111d458a +size 413352 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerDisclosedNotSelected_null_PollAnswerDisclosedNotSelected-Day-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerDisclosedNotSelected_null_PollAnswerDisclosedNotSelected-Day-0_1_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerDisclosedNotSelected_null_PollAnswerDisclosedNotSelected-Day-0_1_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerDisclosedNotSelected_null_PollAnswerDisclosedNotSelected-Day-0_1_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerDisclosedNotSelected_null_PollAnswerDisclosedNotSelected-Night-0_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerDisclosedNotSelected_null_PollAnswerDisclosedNotSelected-Night-0_2_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerDisclosedNotSelected_null_PollAnswerDisclosedNotSelected-Night-0_2_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerDisclosedNotSelected_null_PollAnswerDisclosedNotSelected-Night-0_2_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerDisclosedSelected_null_PollAnswerDisclosedSelected-Day-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerDisclosedSelected_null_PollAnswerDisclosedSelected-Day-1_2_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerDisclosedSelected_null_PollAnswerDisclosedSelected-Day-1_2_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerDisclosedSelected_null_PollAnswerDisclosedSelected-Day-1_2_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerDisclosedSelected_null_PollAnswerDisclosedSelected-Night-1_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerDisclosedSelected_null_PollAnswerDisclosedSelected-Night-1_3_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerDisclosedSelected_null_PollAnswerDisclosedSelected-Night-1_3_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerDisclosedSelected_null_PollAnswerDisclosedSelected-Night-1_3_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerEndedSelected_null_PollAnswerEndedSelected-Day-6_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerEndedSelected_null_PollAnswerEndedSelected-Day-6_7_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerEndedSelected_null_PollAnswerEndedSelected-Day-6_7_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerEndedSelected_null_PollAnswerEndedSelected-Day-6_7_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerEndedSelected_null_PollAnswerEndedSelected-Night-6_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerEndedSelected_null_PollAnswerEndedSelected-Night-6_8_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerEndedSelected_null_PollAnswerEndedSelected-Night-6_8_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerEndedSelected_null_PollAnswerEndedSelected-Night-6_8_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerEndedWinnerNotSelected_null_PollAnswerEndedWinnerNotSelected-Day-4_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerEndedWinnerNotSelected_null_PollAnswerEndedWinnerNotSelected-Day-4_5_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerEndedWinnerNotSelected_null_PollAnswerEndedWinnerNotSelected-Day-4_5_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerEndedWinnerNotSelected_null_PollAnswerEndedWinnerNotSelected-Day-4_5_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerEndedWinnerNotSelected_null_PollAnswerEndedWinnerNotSelected-Night-4_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerEndedWinnerNotSelected_null_PollAnswerEndedWinnerNotSelected-Night-4_6_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerEndedWinnerNotSelected_null_PollAnswerEndedWinnerNotSelected-Night-4_6_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerEndedWinnerNotSelected_null_PollAnswerEndedWinnerNotSelected-Night-4_6_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerEndedWinnerSelected_null_PollAnswerEndedWinnerSelected-Day-5_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerEndedWinnerSelected_null_PollAnswerEndedWinnerSelected-Day-5_6_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerEndedWinnerSelected_null_PollAnswerEndedWinnerSelected-Day-5_6_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerEndedWinnerSelected_null_PollAnswerEndedWinnerSelected-Day-5_6_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerEndedWinnerSelected_null_PollAnswerEndedWinnerSelected-Night-5_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerEndedWinnerSelected_null_PollAnswerEndedWinnerSelected-Night-5_7_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerEndedWinnerSelected_null_PollAnswerEndedWinnerSelected-Night-5_7_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerEndedWinnerSelected_null_PollAnswerEndedWinnerSelected-Night-5_7_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerUndisclosedNotSelected_null_PollAnswerUndisclosedNotSelected-Day-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerUndisclosedNotSelected_null_PollAnswerUndisclosedNotSelected-Day-2_3_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerUndisclosedNotSelected_null_PollAnswerUndisclosedNotSelected-Day-2_3_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerUndisclosedNotSelected_null_PollAnswerUndisclosedNotSelected-Day-2_3_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerUndisclosedNotSelected_null_PollAnswerUndisclosedNotSelected-Night-2_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerUndisclosedNotSelected_null_PollAnswerUndisclosedNotSelected-Night-2_4_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerUndisclosedNotSelected_null_PollAnswerUndisclosedNotSelected-Night-2_4_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerUndisclosedNotSelected_null_PollAnswerUndisclosedNotSelected-Night-2_4_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerUndisclosedSelected_null_PollAnswerUndisclosedSelected-Day-3_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerUndisclosedSelected_null_PollAnswerUndisclosedSelected-Day-3_4_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerUndisclosedSelected_null_PollAnswerUndisclosedSelected-Day-3_4_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerUndisclosedSelected_null_PollAnswerUndisclosedSelected-Day-3_4_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerUndisclosedSelected_null_PollAnswerUndisclosedSelected-Night-3_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerUndisclosedSelected_null_PollAnswerUndisclosedSelected-Night-3_5_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollAnswerUndisclosedSelected_null_PollAnswerUndisclosedSelected-Night-3_5_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollAnswerUndisclosedSelected_null_PollAnswerUndisclosedSelected-Night-3_5_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentCreatorEditable_null_PollContentCreatorEditable-Day-10_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentCreatorEditable_null_PollContentCreatorEditable-Day-10_11_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7f38617a04 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentCreatorEditable_null_PollContentCreatorEditable-Day-10_11_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d331112d89680932e2cc377965691c14efd07dbfac5a10d04f026662dc57c43 +size 52082 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentCreatorEditable_null_PollContentCreatorEditable-Night-10_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentCreatorEditable_null_PollContentCreatorEditable-Night-10_12_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d531f7f1b6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentCreatorEditable_null_PollContentCreatorEditable-Night-10_12_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f130608e52e1941510ef4db2287c54d7da8b50a9b0b67d77400dcd25ed223ed +size 48427 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentCreatorEnded_null_PollContentCreatorEnded-Day-12_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentCreatorEnded_null_PollContentCreatorEnded-Day-12_13_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6f9fefe1de --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentCreatorEnded_null_PollContentCreatorEnded-Day-12_13_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:457325b3f1ccb449bbce167bd215a9760990afa2fee0afeabb0ecf1deba38129 +size 49078 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentCreatorEnded_null_PollContentCreatorEnded-Night-12_14_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentCreatorEnded_null_PollContentCreatorEnded-Night-12_14_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ee352e88ab --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentCreatorEnded_null_PollContentCreatorEnded-Night-12_14_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f56f203e0c87c3ccc0ab1e54321c9b7dcea8bb4c9f1efd0735d43f43b867c03b +size 45927 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentCreator_null_PollContentCreator-Day-11_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentCreator_null_PollContentCreator-Day-11_12_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..efff51686d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentCreator_null_PollContentCreator-Day-11_12_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8209a79e5d20ef58a2e947617b308386d4868799c87039d40fe7c89a68925f2b +size 51842 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentCreator_null_PollContentCreator-Night-11_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentCreator_null_PollContentCreator-Night-11_13_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..33f25a7736 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentCreator_null_PollContentCreator-Night-11_13_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2f174ed9f51808a295fd8b748faa0709fb3ef45ebe9cb7cb093f971edbb11e0 +size 48291 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentDisclosed_null_PollContentDisclosed-Day-8_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentDisclosed_null_PollContentDisclosed-Day-8_9_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..338b81a12b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentDisclosed_null_PollContentDisclosed-Day-8_9_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27550a0227e993f13e343219973e50ad6902737416e90c41b7a1916723c32e08 +size 49025 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentDisclosed_null_PollContentDisclosed-Night-8_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentDisclosed_null_PollContentDisclosed-Night-8_10_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..378fa05efd --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentDisclosed_null_PollContentDisclosed-Night-8_10_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c73a0f3bc8127e9d1491a1056975fa381a0a29aa84f338e14d3c67a8e734918e +size 46059 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentEnded_null_PollContentEnded-Day-9_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentEnded_null_PollContentEnded-Day-9_10_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6f9fefe1de --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentEnded_null_PollContentEnded-Day-9_10_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:457325b3f1ccb449bbce167bd215a9760990afa2fee0afeabb0ecf1deba38129 +size 49078 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentEnded_null_PollContentEnded-Night-9_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentEnded_null_PollContentEnded-Night-9_11_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ee352e88ab --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentEnded_null_PollContentEnded-Night-9_11_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f56f203e0c87c3ccc0ab1e54321c9b7dcea8bb4c9f1efd0735d43f43b867c03b +size 45927 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentUndisclosed_null_PollContentUndisclosed-Day-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentUndisclosed_null_PollContentUndisclosed-Day-7_8_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c57d5709c7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentUndisclosed_null_PollContentUndisclosed-Day-7_8_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:606a328dfb873299cea766f0b48d96aac50c0b9d8486439d1961ff7688162673 +size 47137 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentUndisclosed_null_PollContentUndisclosed-Night-7_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentUndisclosed_null_PollContentUndisclosed-Night-7_9_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..82a52fd27f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api.pollcontent_PollContentUndisclosed_null_PollContentUndisclosed-Night-7_9_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ba7d991e42e9fd16d1dd2b948d40228e073de94d93b829c351ece4596b4ef8c +size 43468 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentCreatorEnded_null_PollContentCreatorEnded-Day-12_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentCreatorEnded_null_PollContentCreatorEnded-Day-12_13_null,NEXUS_5,1.0,en].png deleted file mode 100644 index c8e001c9af..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentCreatorEnded_null_PollContentCreatorEnded-Day-12_13_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:88230f28f7761d8f76bc5a50dbc4ba08a694eaf0d64207257f5196c741fb7b52 -size 49078 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentCreatorEnded_null_PollContentCreatorEnded-Night-12_14_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentCreatorEnded_null_PollContentCreatorEnded-Night-12_14_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 495cb4c484..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentCreatorEnded_null_PollContentCreatorEnded-Night-12_14_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a6e31f773b884608499081ab3c7a27297edfbf8af76f502d9076b4753956e2db -size 45929 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentCreatorNoVotes_null_PollContentCreatorNoVotes-Day-10_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentCreatorNoVotes_null_PollContentCreatorNoVotes-Day-10_11_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 2d9643e989..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentCreatorNoVotes_null_PollContentCreatorNoVotes-Day-10_11_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5fbe23f6b00973c497f791c37f9d884e5d6baa6e6f5376f10633be0cb3043d49 -size 52067 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentCreatorNoVotes_null_PollContentCreatorNoVotes-Night-10_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentCreatorNoVotes_null_PollContentCreatorNoVotes-Night-10_12_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 48329ff374..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentCreatorNoVotes_null_PollContentCreatorNoVotes-Night-10_12_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2a38081492746fdc6a4ae9f5408005456ab0f2143d4f937139d637aa39eabe77 -size 48435 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentCreator_null_PollContentCreator-Day-11_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentCreator_null_PollContentCreator-Day-11_12_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 1ac340b172..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentCreator_null_PollContentCreator-Day-11_12_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:09440b685e219b4a5f5dc78d2694707d7eeb1905d6fdea6395e3dcc941482ac5 -size 51846 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentCreator_null_PollContentCreator-Night-11_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentCreator_null_PollContentCreator-Night-11_13_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 7c14f911dd..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentCreator_null_PollContentCreator-Night-11_13_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a0cd0936ace9bac190f21680bf339cc23839e5f5d241522cb8f328b2db0887f6 -size 48293 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentDisclosed_null_PollContentDisclosed-Day-8_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentDisclosed_null_PollContentDisclosed-Day-8_9_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 18764bef11..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentDisclosed_null_PollContentDisclosed-Day-8_9_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:188c362ebd8bc32a47b66a080331db7643cd97714f2d4e952d7bec8c11520dcd -size 49026 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentDisclosed_null_PollContentDisclosed-Night-8_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentDisclosed_null_PollContentDisclosed-Night-8_10_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 8bae8c65fa..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentDisclosed_null_PollContentDisclosed-Night-8_10_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b4b00894025844927932e790a1738c85cfbb61e61a81dfbbbd7e342f38f40b99 -size 46061 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentEnded_null_PollContentEnded-Day-9_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentEnded_null_PollContentEnded-Day-9_10_null,NEXUS_5,1.0,en].png deleted file mode 100644 index c8e001c9af..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentEnded_null_PollContentEnded-Day-9_10_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:88230f28f7761d8f76bc5a50dbc4ba08a694eaf0d64207257f5196c741fb7b52 -size 49078 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentEnded_null_PollContentEnded-Night-9_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentEnded_null_PollContentEnded-Night-9_11_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 495cb4c484..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentEnded_null_PollContentEnded-Night-9_11_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a6e31f773b884608499081ab3c7a27297edfbf8af76f502d9076b4753956e2db -size 45929 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentUndisclosed_null_PollContentUndisclosed-Day-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentUndisclosed_null_PollContentUndisclosed-Day-7_8_null,NEXUS_5,1.0,en].png deleted file mode 100644 index b813fd723d..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentUndisclosed_null_PollContentUndisclosed-Day-7_8_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cc814fc52a37c0724ac5a45a0337af25a2371ed8c4a0d4ce4f899bfb0a6c2ef5 -size 47138 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentUndisclosed_null_PollContentUndisclosed-Night-7_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentUndisclosed_null_PollContentUndisclosed-Night-7_9_null,NEXUS_5,1.0,en].png deleted file mode 100644 index eecf7113c0..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_PollContentUndisclosed_null_PollContentUndisclosed-Night-7_9_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f5f8f51b498725d57c6086741ff07123c504c81c8c2bfafd483130afc2559b35 -size 43471 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_0,NEXUS_5,1.0,en].png index 68a1dc84ab..42cc37bc7e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf95dbf44f917fe16223fa42562c6ac7241f3392c5b89ff0efc4c004fa081252 -size 34773 +oid sha256:a79a90406a98deaf70fd3234dd1f2bbd51060b1074a5a285d3315bc90191bcd6 +size 34775 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_1,NEXUS_5,1.0,en].png index dcc3303e35..d369206aa9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c814e41a183503c94a0631c0653f44d3ac7908c4db1d7ed5e04d9cce7cdaa83 -size 39180 +oid sha256:2a638452c7d6554a534325b4141bf35aaeec817eec8ebbe50a4efab077164b88 +size 39183 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_2,NEXUS_5,1.0,en].png index 72d9e37ee1..44fe8a5840 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d089fbf26e86aefdecb2d5ab04f177099fa414c9d2a13e9ee0076afc49ded5d2 -size 40296 +oid sha256:d97257fbbd85d70a319795af9738a3773892d96c6b64e9e8793e693d70929e33 +size 44178 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_3,NEXUS_5,1.0,en].png index 69599297ab..78f1085afe 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3b26e20df7cd33893b53d0b512c14afda8889696ecb9a3f8afc645de339f6b7 -size 47180 +oid sha256:70c14eee23c6654149623900aa1edea45ad52e09d21a602af56804f6b8c6d8fc +size 47182 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_4,NEXUS_5,1.0,en].png index 3e31b627c9..a32c442bfb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8317bc1617bf491b5a1c6c71db7f9a26a96fe520cb6240baf3bf454288cc6722 +oid sha256:2539cd3bb3a178fa3e64a7832811e693ebe5529c4923edb185af09f49d7f00ad size 29604 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_5,NEXUS_5,1.0,en].png index 16c9d3ce40..9d35b240a3 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:365b87e9487595f51cca28523d01ef222b2ab1016c025fd86df6efe9c6058ecc -size 124268 +oid sha256:1b133f65f39ef16d52f4f92e33cb1f38e2c7ac22d61a146ba54937b2d903d54b +size 124269 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..dd72111031 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e545dca45645bf7874d183697250ca6f8f916121e5a72a07409917a62d1dc94 +size 35440 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6f8432a888 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Day-0_0_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:362bcdd3b7e32613da7b9f1a6a2dc23a097e8e9b671d185220d9a243326c5b4f +size 37556 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_0,NEXUS_5,1.0,en].png index 058ea34e33..93b2003a01 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:998aab1eadadb0f45da7f3f28c19a667c150bc6a7c6db8cb5600d1be75636b05 +oid sha256:bcb1ac2705ac87789c3fde971958bd4b719ce2e303ea80b93e3a1ca56959b880 size 32882 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_1,NEXUS_5,1.0,en].png index fa614f7c39..b90a1f6af0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00123f8e5a2c7900da1db0d28c8364f5dbf9cef733f5e6ce9639090a10c6b214 -size 36426 +oid sha256:5bda72b3a7befebd49870718c7bfe305ea6fb9b9b96079ad481dd104d0ef05d4 +size 36429 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_2,NEXUS_5,1.0,en].png index e6182ffbae..42aa83f958 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8b63e78ca18401ac3df3c66057c888012d1b5b0eda0befc19ff5c22e4d57f51d -size 36011 +oid sha256:80d6f893a6d3d01518c381eb5e7a2ccd261d000ee64fa8eff81a6805f24f5bf5 +size 39383 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_3,NEXUS_5,1.0,en].png index 8f23191d70..386d8efc61 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:951dbf53f6f96aef3a8679e58aebe700d7f00875e915a80571d245cdc9c4e7dd -size 44325 +oid sha256:2b5e4dc970dae62a44231387a2c77b355546553cb0147ca86d2e62fba3795009 +size 44321 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_4,NEXUS_5,1.0,en].png index d372a42ea2..7934af8a68 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d696d56a265ff539dcfbd5ba3f6ac28a4d8f278d8dfda1f3a369caab05bd410e -size 27870 +oid sha256:8b0e5e5d17571b37b764d699831a44d461aa4237ac3232bf9b1541351c0bdf29 +size 27871 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_5,NEXUS_5,1.0,en].png index 83c6c6b6a0..6f1ad447a9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a0fb1a13df4ff6bc99a2f91e00bb6b9dd5338b3c459c7467f68fe6a153b6bb1 -size 108687 +oid sha256:be7fbaed8c51b8aaa13875db6b73eed0939beffd62565470dbd6a291fdcbbca7 +size 108688 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6b1952a412 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0ccb4cce1db9083561047204aa570727c870a2d1974534fbfc8482b3eaf4fa53 +size 33628 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e6c69623e8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_CreatePollView_null_CreatePollView-Night-0_1_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9fd2419fbcfbf5df4847e47416f3717c1176c5e9e5b529503916f6413c47e5cf +size 32712 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.history_PollHistoryView_null_PollHistoryView-Day-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.history_PollHistoryView_null_PollHistoryView-Day-1_1_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..fd4b70813f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.history_PollHistoryView_null_PollHistoryView-Day-1_1_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9900ded970610627436437fbbfa40ec41dba86edc8f8876f769870995156b995 +size 59859 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.history_PollHistoryView_null_PollHistoryView-Day-1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.history_PollHistoryView_null_PollHistoryView-Day-1_1_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..03bcfda09a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.history_PollHistoryView_null_PollHistoryView-Day-1_1_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c003aa7b3cf6d48cb33a8c33011293cb218b7c9cc4673613c9dedb58e5b3eb13 +size 64090 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.history_PollHistoryView_null_PollHistoryView-Night-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.history_PollHistoryView_null_PollHistoryView-Night-1_2_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..87f6d2838a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.history_PollHistoryView_null_PollHistoryView-Night-1_2_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d82750e7225b54b68b409e0a02281cbf2ca8832db0b2378cfde79d13aa406ad +size 55862 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.history_PollHistoryView_null_PollHistoryView-Night-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.history_PollHistoryView_null_PollHistoryView-Night-1_2_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..483cb3715c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.history_PollHistoryView_null_PollHistoryView-Night-1_2_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:25634fb44c842072f72a677d5f5de6e961090da3a06e84d1e5744508ccf22029 +size 59677 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_AboutView_null_AboutView-Day-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_AboutView_null_AboutView-Day-0_0_null_0,NEXUS_5,1.0,en].png index 97f8aa0030..ec3d9a5d20 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_AboutView_null_AboutView-Day-0_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_AboutView_null_AboutView-Day-0_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6113823a0f37c6ac559d895dd7e07c8200bfbf5efca8fbc32904a8326512053 -size 17275 +oid sha256:245ff24f40d963f5af140363dc466ebb6de17a15c0ab747996edce4e93c7aa4d +size 17279 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_AboutView_null_AboutView-Night-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_AboutView_null_AboutView-Night-0_1_null_0,NEXUS_5,1.0,en].png index 7272afb056..ffde8437bf 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_AboutView_null_AboutView-Night-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_AboutView_null_AboutView-Night-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2014c0ffff49cfbe684902c35719c96452cbef7f164f9017409702da497fc70b -size 15988 +oid sha256:98c206830e2bdc01c30fb341f344bd527db9dd1873bf5ff4f8e2d7a5a1746c2c +size 15993 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_0,NEXUS_5,1.0,en].png index a8b6a93bdd..df29aa76c6 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d6bc45093c8a96a5446c18b42590d6b866a40ea34268e4764a29505dcf97965c -size 42565 +oid sha256:592c1cc5bc0e8ac2912d0cc9d0fa2aaeec1ba5f49a23415ff4abe16634f7c9f7 +size 39896 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_1,NEXUS_5,1.0,en].png index 05647b9c50..06bad1718a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:290bb5d6134171c6d3f343ee3bad95d244175fd18696c5c27a526b9d61948d5f -size 42232 +oid sha256:504c3ab78b9e10fe8c26d003f487397835d1c548ec11e624bb1317500282d6a0 +size 39415 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_2,NEXUS_5,1.0,en].png index e8a91dae18..6c581d5c97 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9ee0f94d9067eda01bc4b93bd5bf5c7493d0f6790bd5c1d1d542ed029e0a07d -size 42149 +oid sha256:22939cc1060893e6440f755701303be0fb8c8c1c39256af1ce7b12a1289f7f4d +size 39391 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_3,NEXUS_5,1.0,en].png index 33ebf89295..48b42a7ee4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd3770364cdfb8804008c0cd86129e689f239a4685ec4690b2d8f65f73b57ac4 -size 36621 +oid sha256:9e08bcbb2d09af6d796068a4c64ff5f03d73aadefe3a20f0a2793333f73291f4 +size 35414 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_0,NEXUS_5,1.0,en].png index ffd6bc821b..9652e4df09 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:917073ea5c55d001279b622a268a61ce2b56b94d396c99802f92013b6ddbb71c -size 39924 +oid sha256:fa224e2d1273a69ea0a3d56765d2dd149b7d295f576c43c09c1eceee92a5c528 +size 37182 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_1,NEXUS_5,1.0,en].png index bf66ea4700..cf646c4351 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e6e41d8672db7f6b89f3db5e174569c9ccec381e0f3c34ab410a85768234764f -size 39683 +oid sha256:9a03a4ba4a0a52dfb0c90f4fde244776222df5d00b76f4a41259f13f7847d81e +size 36850 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_2,NEXUS_5,1.0,en].png index 0fee0836a4..48ba4870ab 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:577f6db9a8a987a685b3596644b0cae992694662aee10b5e64b4c748e21e1a5c -size 39647 +oid sha256:adc64ea11ab2a5ac4e1533e24ba1c7a75cc05d1722b26f949a9d97853b75450b +size 36795 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_3,NEXUS_5,1.0,en].png index f194912300..49dfb9077a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d14440500ffac475aa57cedbc3de507f182cb1e79b0b93b4f4b9b90c3c5c13a5 -size 32136 +oid sha256:cf35986b43c0565658655241243d0704000e79131eb4f2291c2da8d66f014eaa +size 31009 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_AnalyticsSettingsView_null_AnalyticsSettingsView-Day-2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_AnalyticsSettingsView_null_AnalyticsSettingsView-Day-2_2_null_0,NEXUS_5,1.0,en].png index af8f8b8364..c8c9509170 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_AnalyticsSettingsView_null_AnalyticsSettingsView-Day-2_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_AnalyticsSettingsView_null_AnalyticsSettingsView-Day-2_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cbe0660d1c39f4e9fb3db31b27bdac3d77a516ba7cbfe1de599a27b4b4d2b416 -size 26380 +oid sha256:23f5026bb468e841c9647d6ccc2cdc6eac52b18e5ad21b2c45192f6519141ff0 +size 26376 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_AnalyticsSettingsView_null_AnalyticsSettingsView-Night-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_AnalyticsSettingsView_null_AnalyticsSettingsView-Night-2_3_null_0,NEXUS_5,1.0,en].png index 77c1624a6c..e8127ff0ec 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_AnalyticsSettingsView_null_AnalyticsSettingsView-Night-2_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_AnalyticsSettingsView_null_AnalyticsSettingsView-Night-2_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e150198c0568db5ce52811727df66406974795c4e3d39b67a13d55d2d5552ed -size 25179 +oid sha256:a8923a5a2b15b5cca3c9c77ebd587b60c7dacd7d68d04d9f8f671de2175d997f +size 25180 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Day-4_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Day-4_4_null_0,NEXUS_5,1.0,en].png index d66b4d0ff6..95be5a188b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Day-4_4_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Day-4_4_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26cc783e78ec99e26b23cd7623ba6cd145af5efc548ac0ebd7380051be3d441a -size 35350 +oid sha256:093a2b69dfa7bacf90a2cd7e1d6643f008539166563bf19e3e544560d8c5c345 +size 35370 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Night-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Night-4_5_null_0,NEXUS_5,1.0,en].png index 7e38d91520..78ec8a14c5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Night-4_5_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Night-4_5_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b17880c0e49d06ab6f015b64c286c5ba75d23f74e12ca2876112cc44a0d2421 -size 31611 +oid sha256:7cf27bfc83479f38722ad245a48fb5f0d64d47fe58f703c639cd3a9732e7d588 +size 31637 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_0,NEXUS_5,1.0,en].png index 89d469e66d..ee1ffaaee8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b040737c81fb04596312a207e58c75580e48fdd70c1f5ee6abd3c73a846c0337 -size 58489 +oid sha256:b33a42b7e1b50f6a61c1753669ab59a64f8f54d8dd98acc0889dff39e5a5d1ee +size 58487 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_1,NEXUS_5,1.0,en].png index 89d469e66d..ee1ffaaee8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b040737c81fb04596312a207e58c75580e48fdd70c1f5ee6abd3c73a846c0337 -size 58489 +oid sha256:b33a42b7e1b50f6a61c1753669ab59a64f8f54d8dd98acc0889dff39e5a5d1ee +size 58487 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_2,NEXUS_5,1.0,en].png index 312f3bb41c..faffa68560 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5b01d0d26eddb4b6edd900f0f17568d76108e4be97c979d63c5dcca1ca0ed83e -size 56942 +oid sha256:a968550e3b18553da13bed331ecafe03aa617aa8d16ae60bc7beb74b9ad43ee6 +size 56941 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_0,NEXUS_5,1.0,en].png index 0682509a00..67ccb5c23e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:491beebfede0316e804b818c65794bd0c8f8125fc94f4a89f8e1b3ded5afbf3a +oid sha256:b912ae1bf6b013c9136311b154cd05663df9bed2ec2f9e7874206e23f76c544e size 53597 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_1,NEXUS_5,1.0,en].png index 0682509a00..67ccb5c23e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:491beebfede0316e804b818c65794bd0c8f8125fc94f4a89f8e1b3ded5afbf3a +oid sha256:b912ae1bf6b013c9136311b154cd05663df9bed2ec2f9e7874206e23f76c544e size 53597 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_2,NEXUS_5,1.0,en].png index 39d9744130..ecfb204136 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:16b24b31812e18fbf54568f2bae63c0827dc30e73d7924cdab6387df0c2ad41b -size 52062 +oid sha256:eedb5b5d1eac2e03ead89f9f5b76f6812a82d9b529e5e6de6eea6a700274aaec +size 52061 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Day-7_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Day-7_7_null,NEXUS_5,1.0,en].png index 2d9578c3c2..dc66dbf5c9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Day-7_7_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Day-7_7_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:954c763c082f8e4016247643408a61a038a221bb84643ba7e80f3389818528ba -size 15587 +oid sha256:decba5aecbaf17385c6b92396b48ba7e8cb1d68c8319bef3334415cd42fee828 +size 36034 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Night-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Night-7_8_null,NEXUS_5,1.0,en].png index 8a32c8d742..d0bf3ae9aa 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Night-7_8_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Night-7_8_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30886fbb00695077a80e046cd6c6c134375be8ecb8c3962e72fdc0dff60d7069 -size 14194 +oid sha256:97aec529c58486f1686e6c15f0cb9ed5286ed169115d8053b734bc491f724a28 +size 33309 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_0,NEXUS_5,1.0,en].png index 7e1e8e88a0..af36db4a67 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11aa649bb8e25975c79ebc8c20aa82153acac75b4087f99485c58bd604ba3f33 -size 35947 +oid sha256:b927c63f95092c5345ceb35506e160c9ce6ce0348bba33d258c390cd8bd158de +size 35825 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_1,NEXUS_5,1.0,en].png index 16d4767fba..f52f17c794 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9bfce546aa824f56a1d89d638fc1a2ed6fc02da326edc5360d3618795c58545a -size 35875 +oid sha256:6886af0b4f9e674aafd2674c6b77e707a08fb4a0f626169581ed3245b4241972 +size 35751 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_2,NEXUS_5,1.0,en].png index 4830f2db94..aa0300ea79 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:addc7ec085394342bdd8f072f8d65ff25208bc1352b0fe1d3b9109bb4998424e -size 32286 +oid sha256:ef4a2ee8e94f57164b5834fa331c37a3bf7bee15aae17fa3c5fb2a9c82e3e3a9 +size 33508 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_3,NEXUS_5,1.0,en].png index fdfa653f1f..ab746a0a59 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca186fcad4d0a39f4f6108cd4c050e5ee692fca17168a1a44393b7aded6126dc -size 36827 +oid sha256:c1acca39f67a574b9b467827515b2f3442b4bb532a6d2cf3ad611ae79098815d +size 36659 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5455f6dd66 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_8_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78eab361860e4b4153a680fad703dc61c69c80a28af0a90b0a6add85178eea4f +size 49828 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_0,NEXUS_5,1.0,en].png index 6e3219983b..a199e849e1 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1df0f8620db5a3f751e37543f34785517703e4731af9e624dc50b91de79f46e9 -size 33107 +oid sha256:99b99dd9caaecaf34f5a354251833375456c43e4c31bb2dc020b8caa396fe7e3 +size 33088 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_1,NEXUS_5,1.0,en].png index 7290142a52..b20052234e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a58daa364819cef83467a57974517118d683f6b47ae0ec19164dc0678cfa63c6 -size 33064 +oid sha256:c427f18e4dcb5539069bcb6cb2d81160208a21fa5d1ca0c4086aed27b92013e8 +size 33043 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_2,NEXUS_5,1.0,en].png index 58d70849e6..5c1fbe5dfd 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b551c64c9d99fbdf315662967f0374668e456738e1b2cab44bfc99726f22a60e -size 29033 +oid sha256:5d3d53b374e9b59ec860951fbcd1ddb566c90b3debe335c5cba8f2d9e55f508d +size 30346 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_3,NEXUS_5,1.0,en].png index 18a4bfab1c..a7f1ffde07 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8804c266b0a55c0d3a4e716aa6d9c75f82f2bfafbbd54b8f9475230e20ff863 -size 32294 +oid sha256:0c85782934a98034a75e507f9c4e8316c1aa00e5628ae7fe3092b7f58ae3540c +size 32286 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f731a246d9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_9_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29160e672d2bda2cdb69b7a2c2357316696637db23dc1de0665cc80308303399 +size 47051 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_5_null_0,NEXUS_5,1.0,en].png index 0b21f0ad39..d143e02b07 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_5_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_5_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:06c0841727ce28b358a757133b28a8e3973f99666db02cf8cfa46e5b6e9769c0 -size 52833 +oid sha256:a4b146a10957549dd4410bff3ce4793d25c31910607eec43db44b7be019af068 +size 60521 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_5_null_1,NEXUS_5,1.0,en].png index 84201a2891..b980dba66a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_5_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_5_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5524d336f303c995801049bb32c2ae4ff4511cfe20ddc3709d1cd7a0632eeb8c -size 48900 +oid sha256:340808b11e072679e7b90c939bcb5367a723b9881f084dccc0b594de5f12d543 +size 54830 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_5_null_2,NEXUS_5,1.0,en].png index d5919c0743..dc7c28e222 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_5_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_5_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:80c8d43a01b2d39d5305635341329f863edea75d5adc0bf3989a63d9d38d46a0 -size 48814 +oid sha256:ff00bafd64a4827472d757e6519955550327bb9dca97ea8a7163da4ce278f445 +size 54777 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_6_null_0,NEXUS_5,1.0,en].png index 2dd0f18938..3b40c8ceda 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_6_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_6_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2271f5652918a2c1d3e55e7c1733481ca5f535d64517998296cc8c00fec3cb7 -size 49180 +oid sha256:d0b8f14d6bcd6309cf8a961104738c529164621a2de4c642ce7944a48217d609 +size 55838 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_6_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_6_null_1,NEXUS_5,1.0,en].png index 40351f203f..68440b467a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_6_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_6_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c14d79d561656b6ac874d6cf54a731626cc221b302c99ca002cd25e9dd8c55ed -size 45047 +oid sha256:964fc455b25f442bfc89e917148c15f7f9301d9b6db5c94f2075d994b74a6eef +size 50975 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_6_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_6_null_2,NEXUS_5,1.0,en].png index b484ff77bd..a6ff4a3317 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_6_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_6_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2fc6852036964b9519a9be8eb171b6fce4611f9575208d47f70e21e945bb18da -size 44401 +oid sha256:fccd2b3e6fff431b0b650cccef21c0b8129c838dfc3b1b0bc6ee83321dcfe698 +size 50386 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_3_null_0,NEXUS_5,1.0,en].png index 197546dc67..b2db57cdb8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b5c68ea146c7f1c3a8e39a6ecdb51e95fb99e371e4f2bb976fdde7d5849db071 -size 45105 +oid sha256:781e626db4e9491d1fd9d8098a6e62ece4d7034e1447d168583bdc5a308f371d +size 45249 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_3_null_1,NEXUS_5,1.0,en].png index f2520abbf0..5dfbf46f31 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c1cc43c5157a456317171b85c98042dec9865d21121a0663530fda1fe1236a3f -size 44434 +oid sha256:3d158b7003c5f84d9aab2ca85bcdce6011ce7ce7c1eede42a343d460610eddeb +size 44573 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_2_null_0,NEXUS_5,1.0,en].png index 6995463dd3..8c46bc7976 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d4d1146622da22dd72d53d9d5e239b6f45d23ae4f3ed42f2b0d0902f25256569 -size 48168 +oid sha256:c9d7de855fc1442535920061110c4b7dd71535f90dd77e55469fdec1941b5d9f +size 48266 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_2_null_1,NEXUS_5,1.0,en].png index 3c0beefa8a..65f2b0c30e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ad8dcdb0292dcdeed9c50d8abe2bc0bf09c728cac11bfddeac6e74512ee224e2 -size 48068 +oid sha256:bab34cb8a11b8056c5f4a0ad10003eec4c2f5dbb128b4339e12c5687c8ade2e5 +size 48167 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Day-10_10_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Day-10_10_null_0,NEXUS_5,1.0,en].png index 096c9b98af..4c1d25e603 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Day-10_10_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Day-10_10_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:066b94b72b0f5ee1be0e8eec636e927a5cb38d17e1a69ddf6355e588e60cbec0 +oid sha256:3ec39a210cd351fe41d86f14982145c4e6f39aed1f02eacd19faf8aa10ca3d2a size 22538 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Night-10_11_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Night-10_11_null_0,NEXUS_5,1.0,en].png index 64e940f97c..a700696ca7 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Night-10_11_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Night-10_11_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86b8fa4f3b1b40e34fafe861c74ecdbb4a21dbd970f1b2445c9326d936e55630 -size 20888 +oid sha256:81b046556a91cfc4781b679b9d99d05865a3cef99ea43d6bfa64bd963c02c587 +size 20890 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index b7bb6bcf4c..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3928ea1a60bf2b5c1cc94ef566ca67ce03679994430f826f50636c781976e8e2 -size 70068 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 7a638cc9f8..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:62ddb0f9d6764b639b1a1438535c19138d1f81afdaaebfe03b021fc7f64a5291 -size 206670 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 10d0f335e2..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6d546a6a3a5e517e99758f05396a72cc7bf802f3e207f5c7d881e5805f103b67 -size 61356 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index b7bb6bcf4c..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3928ea1a60bf2b5c1cc94ef566ca67ce03679994430f826f50636c781976e8e2 -size 70068 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..85063d3c6b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9ae7fef08fe8f5d08892406cc4c979cebdc8e7751dfda78bac85f4bb0956c245 +size 70071 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e02ea695e0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3fc0cdabd97d39da8369f1945b9c3d7c9c05baa6da83c029c8ff99ddd8c8b300 +size 206672 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..0ed580ca73 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e4e898a358de8246f7a835805994f9865e519a894b426f9a1234a998d13c54ab +size 61356 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..85063d3c6b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_1_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9ae7fef08fe8f5d08892406cc4c979cebdc8e7751dfda78bac85f4bb0956c245 +size 70071 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index d8461b8165..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:53cb4046ffb2391e880bb9f4567534483f9c3e8bdbea7d7706d3fb93ec0acdec -size 67105 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 8e8c828451..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:22c01def033c5ce1e7bc12dc7b5ec6fae11bb827f29818d3921f156a4cf04386 -size 202460 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 29f1413708..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8960e097e94be82ab519bbc1ed3679955dd200349ae32e55d11fad1cbaf586ae -size 56261 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index d8461b8165..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:53cb4046ffb2391e880bb9f4567534483f9c3e8bdbea7d7706d3fb93ec0acdec -size 67105 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a8be95376f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f421bfb415b006742fc889588b983f682b82b63d4c28c8b896e94709672ca671 +size 67100 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c4f73feb5d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c853ca824f30bb99ab2032e87d9f7ebf8ffb187aae769062381db6afce64a9a +size 202452 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2f9f29cafb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca56f3993bc4713aa16e54bdcff24f96804f84dc8e9557247c56caf53fd14831 +size 56251 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a8be95376f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_2_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f421bfb415b006742fc889588b983f682b82b63d4c28c8b896e94709672ca671 +size 67100 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_0,NEXUS_5,1.0,en].png index 93e1766598..490fa7d198 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7550701aefd586a9ab2a4c17ac3d83f6d0055c38f13c8eb07a71fa04abbbed25 -size 30279 +oid sha256:c0f9e19f06d1f3ddd875ad6960b527d5bbe93277a7e178eefbd216f225eac1f5 +size 30276 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_1,NEXUS_5,1.0,en].png index aba49600c7..93099a1c05 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f2205a9500649d1faf74a6a327262b27a8ea51a040a0d98cb8485f5a23e0b226 -size 23616 +oid sha256:61b5cdd557a4a13c25d245011092d5cbf409318009b1786eda0c43d29a633865 +size 23610 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_2,NEXUS_5,1.0,en].png index a72956f4a2..6adb4b807e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f827998598c1927cd389a470b26523741fa81b101af4378f61988012b28d88d -size 55922 +oid sha256:7adcd6f420ca60dfc94005db659c557590116451275c14e31bc6bd58b2ad0fa7 +size 55917 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_3,NEXUS_5,1.0,en].png index 0ff186467c..a7182a09ec 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8346a81bac3012380a5c437f5ad035aa59a195fc486268b62723e704d0c531ae +oid sha256:4ad1243047376718b7e4c94885fe4812fe60c4fe86ccd8971eb7d313e3dbede9 size 29995 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_4,NEXUS_5,1.0,en].png index 152cb2ef59..280729c433 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b47b1dffc574fedd3a8f973ee00b588e9c5d28f232d6f7d11aeb394b642f83cc -size 29999 +oid sha256:5fbd2a68c7cf4d875b09ccccb04e02ce085f1bd7c2d112baa4d394c6a193522b +size 29997 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_5,NEXUS_5,1.0,en].png index ebef8377bd..a306a0d99f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4374b71e56d6fef460876f3f6351091e1d91e3fc74ae09e14370b6213067194c -size 30182 +oid sha256:76a7a71faf0eff9699098fc8970a5c4570db52149a6243bebbe3459af8c88b62 +size 30187 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_6,NEXUS_5,1.0,en].png index 92a05bad78..f7602d9634 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d464efa5c974378b0b9565e6c4ebdaa183a5f92847f4cf8b63f5738972ea0d81 -size 27589 +oid sha256:d1db4c4fe5a5abba5c1801c9b62143aa8b31d89fee5adfc24b2c829efeed00e8 +size 27592 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_0,NEXUS_5,1.0,en].png index fe1b454aae..8ce2497ef8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b259e49d427308ef4fa1c6a39dff305894f50c07251a12610d5c77ec80bad527 +oid sha256:b09146169cdcf3b722150ab1bd22b77515b0f916c985785061615c279824ec10 size 28928 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_1,NEXUS_5,1.0,en].png index 963bb708d9..6e722a0908 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30c33c82a31fb2c2d8f4249fbf3ca1a835ff6364608f668224473fc65704fea4 -size 22609 +oid sha256:c5a2cbba4eaae7f9692cef502726e72a6543ae25c4a5824ad540540d08f30ef0 +size 22610 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_2,NEXUS_5,1.0,en].png index 19c1b05b87..f80d656dfb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9769070b02ba47b3cda529def37ee0b8a926291e951d0c9ec7a2de055cc27826 -size 54186 +oid sha256:41d1117ecec90b4edbf48b41f2d5b918e9f4abba3eadbb2ec968bb341e28baaf +size 54187 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_3,NEXUS_5,1.0,en].png index 6a002be107..a2c2595c93 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:77a3989d4518a63b5ef1f076f4cd6902017fe6bb9dd32b8972b4a0fc86d5464f -size 27847 +oid sha256:a89f05e37d205e01fccc0f0a2e166542b50bb4bd40ba78f946c719da2ffd3ce8 +size 27852 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_4,NEXUS_5,1.0,en].png index c9228eac6d..d1f878e1a1 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2bf891ac65f3fd3f2a1e4f70e9bfd84e454fc547423b45c9c520c1b00d347119 -size 28628 +oid sha256:3253351a8db78df6b711960240a717f17fd9fe1ec557daef6d379bc3a3b81dfc +size 28634 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_5,NEXUS_5,1.0,en].png index 60150a7702..26651a0ad4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe04e244412cd9aa62228b82e4bcb4c467dbadb9ce5a1018a2fc88ada7f8aa5f -size 28044 +oid sha256:c366c35d59d751e588aba8d0bc34673dc1c191a8dbef01e8586483e2337da42f +size 28053 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_6,NEXUS_5,1.0,en].png index cf0421b84d..4760020618 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8621a13a399b87d3a0f0bc68ae401bc3e69b4af45c659cf55fec0530f184407 -size 24272 +oid sha256:00362547931667129d48d3c42d659aac5524b924c1a4e428ce6f834c5c662c4d +size 24283 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_0,NEXUS_5,1.0,en].png index 94e56c3293..23fadd55f7 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e1bb47814ea3c7003183a564684224fec48dae098c95fee95c04ef249249a5ad -size 15578 +oid sha256:e3325a866df1f3d63c695248ee172329f436d91521679ada53c15e34a072a42a +size 15479 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_1,NEXUS_5,1.0,en].png index 390566be7d..f40974ccc2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec95d7886981e8e17430d1afe69fc00f2fb04d6deabcd4cd801b4b1ff52ef9ef -size 30528 +oid sha256:7e264effbf1d64f8a2623dd94f35d8147dc3f4866f3d69ca3adfdc63207b6bb1 +size 30433 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_2,NEXUS_5,1.0,en].png index 5fdec07b67..58c4d58cae 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7cee7fdbad9c2c986354b6f256c1cb7d67d41c21aab9796a5f5e46c7f7443bbd -size 12769 +oid sha256:43e15edc704b5e6f278d29cf80356605045b8cb7f16b48cb38d8d9c3d1140864 +size 12688 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_3,NEXUS_5,1.0,en].png index e481b64ac0..adf3ed447f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dba2c9ede1524451f64d9a9a0bd5c07451848904b6abf3181bfa72609e83c635 -size 28019 +oid sha256:5276aef1eab0cdfda2c2064b1e9786045d57ae51f3ac2aaf21887fc9a991c3f9 +size 27927 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_4,NEXUS_5,1.0,en].png index 76e83846b3..5ba5c22e18 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cece8d5911f6f9aa75a93e566ff99014877cbcb79a6b746cabee4036381d2d67 -size 14829 +oid sha256:3464e46b61b405ad19c711a920a3464388414542d80769e7ce832be09209c3f8 +size 14749 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_5,NEXUS_5,1.0,en].png index e2c77bff36..242f8d42cd 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c8a1a2aa65a07712f00e4c6ce99d1224950272cccc1471389686f0fdefc983d -size 46185 +oid sha256:32a9a3c590d2ea079d0e92290ce9f55de3db56055a7f922d57aef53219f19344 +size 46127 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_6,NEXUS_5,1.0,en].png index 784bbc1183..6f177a2003 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Day-1_1_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f1b7b07c44a12944ec53a2030cc3da0e286d9a4f26ad65d957c91e7ee8ed407 -size 40248 +oid sha256:26377b40db2c1f5b1f818e299366ccf77bf49b5ef74f823101360c6fe24f7aed +size 40178 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_0,NEXUS_5,1.0,en].png index 7677f7e067..327b773a2d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:097db9a6a96fa6216137f36f8ce0a654b1ef40e21afa3e6215de6241d2790ef4 -size 14539 +oid sha256:63ed0ac922e370dc2e87048333bfc32a4d208ea4fcccf1adfab6641fdea88ebb +size 14433 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_1,NEXUS_5,1.0,en].png index 4b9ee5bca0..f1b3867cca 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f387a8541020893ee3853c014eae8fa03eb6cb37f5ddd5401c78087a33568ba -size 29052 +oid sha256:9e1a482977bac69f8b26ad9516d7c37759b0c5713828bf489d6c189567f10abf +size 29003 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_2,NEXUS_5,1.0,en].png index 49a19ec2e1..0b73949531 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4214598beca85afed8a110aefcba75509504efaf3668321c2323d2eecafb44ed -size 11900 +oid sha256:d25d23b1194164edde48b6a1aac9a3df9b10b4a0e6b307dd4aabb90800cdc8c2 +size 11814 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_3,NEXUS_5,1.0,en].png index a0e913a5b3..d0095be8cd 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ef0ccaa515bf802289710d23d350cb16d39c9d3172374bc2417508d86be59975 -size 26898 +oid sha256:998a3e433e20d835300fa1f1c625944262dd169b86b40ff8e49e2b6c982b71d1 +size 26841 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_4,NEXUS_5,1.0,en].png index b261362bd1..d63af540a5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76b256e44e6b45d98bdcd66ad9138c267a3532e8a0b2676f97bc42e01d6d8d08 -size 13916 +oid sha256:0ab8ba4088ab61d4e905f32dac9fe2ead4662d8c6e9bd882b135d468320632f3 +size 13831 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_5,NEXUS_5,1.0,en].png index 6c20eee72a..160eac5bfd 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2a9aad2f5ac1f50a211a31f417849483eff8c6f53955390dc2df93a4dff8f5a -size 45268 +oid sha256:53deb0fc171c1d77101a36703391782ac3660d7acc6c6c6422ba0309c462b7dd +size 45231 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_6,NEXUS_5,1.0,en].png index 1de10c8bbc..13d7526227 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_RoomInviteMembers_null_RoomInviteMembers-Night-1_2_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:afef5cd1a0281c42978678f71b18e33fa48576b5a30b08084c11a268893c875c -size 38438 +oid sha256:f98e68f370e77d00a2578a87fdecddefa12a91f851c030c1451af40ba11bbb11 +size 38409 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_0,NEXUS_5,1.0,en].png index 5610ee9b86..966a6e3d0d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15c066163a768e20c319fcc8358eaf31ce618e617fc3959b08973dab1584a678 -size 19584 +oid sha256:635af24c8306297ac954aa7ddb4286c36904b763e8f32acbbad841334dda8253 +size 22352 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_1,NEXUS_5,1.0,en].png index f79e1802f8..3d6ff401a2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f47cf68a6c9d0ddf05e2e241af48a83348635980b372e60bb8697cebf92990e -size 17388 +oid sha256:c8b5d1c152ed4f3334fc5d6fa74546a1fa7f50dd4975f6f108028a34be0db63c +size 20248 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_2,NEXUS_5,1.0,en].png index 0e4e7745e8..201c88ab4a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:712431fe032e6ee83d0d02a4218242128059e97619f9ff557652efb49dbf35dc -size 19943 +oid sha256:a7caaac71c2c280fc18036e725d9a64aae77e351b494440bbac6f6776658c856 +size 22544 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_3,NEXUS_5,1.0,en].png index 8677b1c43c..cf98fbcce8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:36fca441b0648cce75502538df8e4ac077cc52f7bd4743a22f46a772d90d8d52 -size 36789 +oid sha256:44e2a0e5c2adb1d80c2bf2abfc3ab481ef28a7dd44de8fcdca73d87933219f65 +size 38735 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_4,NEXUS_5,1.0,en].png index 2aba1875d3..ab06f857b9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b60818b3142a0e67d650fc0449d51a7f3eb1199845650551ff3661d773146614 -size 28151 +oid sha256:29a24d3f9c01d866f2b3cfdff1460a47f956c2b30865f1c0c74a505a7ee40581 +size 30824 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_5,NEXUS_5,1.0,en].png index 0663c2391c..baca4fe9e0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe1a6315150c3e372e9fc16cfcaa6f67917c54d9ce529dadd090045a4511c752 -size 20539 +oid sha256:52009b1f8c129638c82568c0d23822cbed4de45f6ce49062aa38a79dfedecbe8 +size 23041 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..03ff1f7b4b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:68ceadfa7696c8350c45bc4c47553e8fe8c10e283ae2a3420a4192160bd97ab7 +size 22925 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_0,NEXUS_5,1.0,en].png index 6c059ccac1..8da2d0200a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3071bfc9bdeb9049e28fa97d13d5b669462ceeea00ea0679db999616ff6c9b3d -size 20039 +oid sha256:52dcc40a72b799510a4400662b462bba34bbb6be98a35eaa5c3edc3a671aa786 +size 23016 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_1,NEXUS_5,1.0,en].png index 058cb80966..7d7ea4300c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:03037f931e2f922b5a1cd58d2000b9003e297605908a25545138972d025baec8 -size 17772 +oid sha256:6099039f33757df49245866f6dbcca9576b216b1fe9e1f87c6c4f4e92add153c +size 20945 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_2,NEXUS_5,1.0,en].png index 2efbac461a..fbdffe0bb4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c875bc994695e3a3b0fe8b9fce149339ec4f0867ce0af1fce040e206f048256 -size 20453 +oid sha256:6f5586f7c60ea5959713a66cb97397c5d834b27100e1603bcbb98c430dc0873e +size 23539 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_3,NEXUS_5,1.0,en].png index c95a613f6f..f76a02a956 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7497745968e1c8cc9d46a3655cc8251f4c92c5f1f929aa6538ad32a395ff6ef4 -size 41499 +oid sha256:10d2ea969c9cd2bcd809e72cc56d8ae086b1b4d848636c55e9db5ef23783620d +size 43647 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_4,NEXUS_5,1.0,en].png index 469b702dbf..350f21eeb0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0aa1c3bde0ddea037dd427d9a98b0563d4df9e754bf078eb52805d6a8967b4c7 -size 32493 +oid sha256:e220d275226ae8820e0119a767b2baf964f2173c430531a379e338bdf9e08adc +size 35530 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_5,NEXUS_5,1.0,en].png index dd4408bfca..86f5985e6f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd437191f000962ebb7f435c44d004530b265515bbaf5dd8af56938e2da074ff -size 21048 +oid sha256:fd3964a31217ccef0ba53ab1f5341311b52746b20660461220375dcad3befcec +size 24194 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b99279289b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1745fbfae1996203b192b07bbbf424eb129944b37b83c0203fcd05c7f332096d +size 25874 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_0,NEXUS_5,1.0,en].png index 42c65ee38c..a384cd763b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f2b33b2220dff71f51c71946e24f0ecbbc9845387b91e8c597457d529d229f1 -size 38928 +oid sha256:7cd9e359060e872c13d56562f8835f11de777e8057c206efb4631b34a84aa407 +size 38939 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_1,NEXUS_5,1.0,en].png index a4b732efb7..c04fd3bf60 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2fb43f26b23c1f7b371c8a8e7a95f08f00015846778f829522986b4e15b40709 -size 14836 +oid sha256:c188a44eae980db02f5ef01020ba16e5e6ea4e484c6d14f318922f37d6091600 +size 14854 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_2,NEXUS_5,1.0,en].png index 282172214a..958e69b64e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:84d07b1aab374b2c2394e9b48687fc79763a46ab2c71462c09e03217b50d8c35 -size 14141 +oid sha256:378b59b58fe7fce9f0ae1c2a5bd5cfb42237722fc5702b34d612123a6a9875b9 +size 14151 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_3,NEXUS_5,1.0,en].png index e4e04b944f..f7947918ef 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2db97913e33cfe4445f033c47343ba2b4be02966090b8768dfb215bc754201df -size 13122 +oid sha256:b41fd5061569d07219009d8fd36c33a2eb8ef48723d24453844a10d56801640d +size 13138 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_4,NEXUS_5,1.0,en].png index 4b8c7c51f3..b44f30cb4d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:99757e5f48eeea2b5797d1379d9d50886cc6f950c1ce1557a03e06295e5b4209 -size 8875 +oid sha256:ad9ec56dd85e7dce08a64b022fa2271c1416f6c27e4eb0d80200bb073de60036 +size 8870 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_5,NEXUS_5,1.0,en].png index e6fb56e1b3..d81d496d15 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1180179773f57495767d85d3b97e5923a75f773156b661abffa51ee280325164 -size 7602 +oid sha256:4731f36d3ee1331c0767ad27d4286feca266deabfd55960a89b0e823925b30f2 +size 7646 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_6,NEXUS_5,1.0,en].png index e0f603b254..c18c17dbcf 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dc3225f8072957e14022237f0e406e269d17933fa486bd0be813640e1bf73883 -size 25263 +oid sha256:f49dd980170c684c0e64b17e6f1a935b37c5fd5d788444923351eeef28644d35 +size 25284 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_7,NEXUS_5,1.0,en].png index 81f59e0fe6..13fffb08c2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Day-2_2_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8ee231d6ded9b516126f0893a475ab08694aeaa1e073caa7fcc0ec57531358a -size 12368 +oid sha256:29f5f872f5e4ff3ef4057253df3eb169e1cdd3b8c3a2854c3a6df1642535115e +size 12393 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_0,NEXUS_5,1.0,en].png index 8441418c73..8c620f955b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:859619d904125c00e9c658280c47ef432ae971c567548b1e8a4ed519ce17687c -size 38230 +oid sha256:9cd1b17ce3bc6a0eee58fedb532f6f1c87a6fa2130af517cd0eaa9b0864bb193 +size 38242 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_1,NEXUS_5,1.0,en].png index 021972dd8e..be115167a4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0003b5e5481ff20d8578e8581da478060ea8b329c9f9e9b976a010c288e4b85 -size 13957 +oid sha256:c6e1098067816b2adbd4bae1a0ae2e1f8299cac0fcccfa17bc7423b6bc339d3d +size 13972 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_2,NEXUS_5,1.0,en].png index 6b542cb9ca..7c55f7c532 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:881c635e0ae757d88120da498f3bd5c3c6cef2499ff2c205262f1871cdefad86 -size 13192 +oid sha256:809408ea8ddcac0d8f8c75db1b0d4a0c007c42f746a67838f48f14f6abce5cd5 +size 13199 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_3,NEXUS_5,1.0,en].png index 411697aa20..0404884a2d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d1e84c09381a653fe1f30b231989d493218bcecbcfba20d7ac6ff4c1e8ded12 -size 12318 +oid sha256:508967d698d39e29229a1ae898a0943a10f5a5d6e49740bc8e12bb267b2fdf62 +size 12328 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_4,NEXUS_5,1.0,en].png index 857bd27b17..4b8056e210 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55167302862c369542fd6541bd376e28aaadfd72d45f1d0a41c961bc020d3302 -size 8622 +oid sha256:1e79ddfc0d0b6bf364e882a28cda0407588fdc81e5e1edc4d39283e7bf9aa834 +size 8623 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_5,NEXUS_5,1.0,en].png index afad0f4668..573ab912c2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4eb4cb9406ff5afa7f562782e221a07397858ba05beb000e62cc4608d02e24c -size 7204 +oid sha256:ae2119fa8d67dc6787a3cf86ada1259a4ff941d1d9a55936d5ee84e71e42ac72 +size 7217 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_6,NEXUS_5,1.0,en].png index 0214142895..5a095b8526 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6bb44f2aaf3cc5ff536fd12f8b3d1894fd7287a303ff4fb532ba3a4e4ca263ea -size 24909 +oid sha256:385797da9282939c69d84d4d2a488b0df5eb32260ef17750e97928a2a670c907 +size 24937 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_7,NEXUS_5,1.0,en].png index f0d073a0a1..b89888f383 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_RoomMemberList_null_RoomMemberList-Night-2_3_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f96ce0b4f5b2ae5f1aec3fc9594d0430762e25bfb4305b1af73167228050e8d1 -size 11560 +oid sha256:deee6be545672348d4cd04921d69cbb8a83dfb23f0378df8ab564f536682de3b +size 11565 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_0,NEXUS_5,1.0,en].png index 7a08009659..7e3f549840 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f2fc33febab98860da9b6591c4ae33dc9ba4fa28365520e4fcb08f8bfaf6ff8 -size 33828 +oid sha256:695a934529456a82e99371e9b53231ac02f6d235174927fbb346bf2a5375aceb +size 33962 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_1,NEXUS_5,1.0,en].png index 383adaff52..83a9e17db0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d99786d84445bc42f7e7f7e414f973a04ba6684780f69eb1cfe60a09ff3ec6e -size 38017 +oid sha256:4e406c5e173add3b29c3ad7a10e968d3bffc4627c7895bd2080826b832652ce1 +size 37783 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_2,NEXUS_5,1.0,en].png index 2f2e6b821f..079518de5a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4993368d2f9e605674efdf3027314a312428ddbda78e3168612fd03de220a581 -size 33874 +oid sha256:52fe76d305e114d5eeab85b608c5d86307ccd6fa0f283f4a806d64163d17c7b0 +size 33985 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_3,NEXUS_5,1.0,en].png index 14bce95be5..2d44a91eb7 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:315b8d8a5081cf2aeb3dcf50992863c1c0e3300d3825954e1a12908567843d58 -size 41165 +oid sha256:6e91c8c83e1a793bb8e0d306b98a4737187a5a5df7a47c67ac6a716696cff3fc +size 41208 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_4,NEXUS_5,1.0,en].png index 2f2e6b821f..079518de5a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4993368d2f9e605674efdf3027314a312428ddbda78e3168612fd03de220a581 -size 33874 +oid sha256:52fe76d305e114d5eeab85b608c5d86307ccd6fa0f283f4a806d64163d17c7b0 +size 33985 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_5,NEXUS_5,1.0,en].png index 14bce95be5..2d44a91eb7 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:315b8d8a5081cf2aeb3dcf50992863c1c0e3300d3825954e1a12908567843d58 -size 41165 +oid sha256:6e91c8c83e1a793bb8e0d306b98a4737187a5a5df7a47c67ac6a716696cff3fc +size 41208 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7e3f549840 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Day-4_4_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:695a934529456a82e99371e9b53231ac02f6d235174927fbb346bf2a5375aceb +size 33962 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_0,NEXUS_5,1.0,en].png index d59805dfbd..c38576522a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0cf8d93229fd5d8034dc7e2f68f9868fc42515c538098c89d4dddca814827bc3 -size 31448 +oid sha256:a128bd56c653706f65f20dac1e4110b21259e76821821057fe1e829044a1d36a +size 31523 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_1,NEXUS_5,1.0,en].png index 9b7a1f3917..3f54ca9fa5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:13c5007d3a97734dca40a88f2312e59d09a8906deb71d16c0611df7dd25a6391 -size 35124 +oid sha256:65bd05d8176f7d65f10ee3183f221afc73a759962a2e6b95067d3830a93d876c +size 34766 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_2,NEXUS_5,1.0,en].png index f5ee587b5f..1b92bf8947 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2351abc0cbecc9ff503ba50a1d9c01eda5e771181335d5d1b0fc72f00b337c3 -size 30586 +oid sha256:f00202b17ca6c89dfdefc616888ceeb7272cfdb452745e8e034d556764983f7d +size 30717 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_3,NEXUS_5,1.0,en].png index d3095ecc13..52d4323c1b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee987e41ce37ff52cf8265fc2ebddfff18b6942e624d40b418033a27531874ed -size 36623 +oid sha256:fec18df4d767d669a62d9adcef3d919332fb3af72f5bf2df349acc3e964fa78e +size 36634 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_4,NEXUS_5,1.0,en].png index f5ee587b5f..1b92bf8947 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2351abc0cbecc9ff503ba50a1d9c01eda5e771181335d5d1b0fc72f00b337c3 -size 30586 +oid sha256:f00202b17ca6c89dfdefc616888ceeb7272cfdb452745e8e034d556764983f7d +size 30717 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_5,NEXUS_5,1.0,en].png index d3095ecc13..52d4323c1b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee987e41ce37ff52cf8265fc2ebddfff18b6942e624d40b418033a27531874ed -size 36623 +oid sha256:fec18df4d767d669a62d9adcef3d919332fb3af72f5bf2df349acc3e964fa78e +size 36634 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c38576522a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomNotificationSettings_null_RoomNotificationSettings-Night-4_5_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a128bd56c653706f65f20dac1e4110b21259e76821821057fe1e829044a1d36a +size 31523 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomPrivacyOption_null_RoomPrivacyOption-Day-3_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomPrivacyOption_null_RoomPrivacyOption-Day-3_3_null,NEXUS_5,1.0,en].png index 38c517ea47..a0944fea6b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomPrivacyOption_null_RoomPrivacyOption-Day-3_3_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomPrivacyOption_null_RoomPrivacyOption-Day-3_3_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:62418ebe7afa2c1c7eda2b1251fb189a75527aa7a4289a9478c531fac7dee8bd -size 10666 +oid sha256:aa7de6ce541afcabc79fc1554809c217fd4f8bc83ed2c324342a9d28b9f34717 +size 31001 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomPrivacyOption_null_RoomPrivacyOption-Night-3_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomPrivacyOption_null_RoomPrivacyOption-Night-3_4_null,NEXUS_5,1.0,en].png index cecfc09f64..d9f5079c1a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomPrivacyOption_null_RoomPrivacyOption-Night-3_4_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_RoomPrivacyOption_null_RoomPrivacyOption-Night-3_4_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e5a32b1baaaad0bf266e190084a64b3d382a2d6d472a55b1c3653ee948e100c9 -size 9756 +oid sha256:9c7bdc732cc76ffcded6903e533ccb64d57d671f02a62093e63d0375fe0c680c +size 29016 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettings_null_UserDefinedRoomNotificationSettings-Day-5_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettings_null_UserDefinedRoomNotificationSettings-Day-5_5_null_0,NEXUS_5,1.0,en].png index e4ff848c76..4833897e83 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettings_null_UserDefinedRoomNotificationSettings-Day-5_5_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettings_null_UserDefinedRoomNotificationSettings-Day-5_5_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e7a76c0f4fe32692980089d2be095d9206c8fdec85aa2e7a057829d027b396e -size 24069 +oid sha256:78cb940e6edfe0591071224fc7a8dcf32472633f56c38d55647dff40b042894b +size 24303 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettings_null_UserDefinedRoomNotificationSettings-Night-5_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettings_null_UserDefinedRoomNotificationSettings-Night-5_6_null_0,NEXUS_5,1.0,en].png index 2ddb87f0ba..edb45cf46d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettings_null_UserDefinedRoomNotificationSettings-Night-5_6_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettings_null_UserDefinedRoomNotificationSettings-Night-5_6_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4d30bbb3b982c47a3632d2b7350a8e63e1c3430d4e588297c2ea55b5d5ecb94 -size 22642 +oid sha256:52992127004c26595f2bfeb50c657a5628b18ffc10b8e6b260203504f44756fa +size 22787 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_0,NEXUS_5,1.0,en].png index dcdedbb448..7306748ca0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc3b73a8d026391bacbe66f774b2464a8455f35170bdd7e9316f06267b8913d7 -size 52410 +oid sha256:d21e45a40de75a8ffc30e574f3f9a1f308616c5b9bf25de36d5277db5911c373 +size 52396 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_1,NEXUS_5,1.0,en].png index 6b2561f178..e4e301ae45 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce0882664a2cbf8d951003943ed36468e1e90b50ed8aca6ae3dfd87671c79899 -size 49421 +oid sha256:55ffd8384b2b9bb21530512b0794afae3312a3344522973c200009f22b319a1b +size 48550 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_2,NEXUS_5,1.0,en].png index 10c0da6c3b..c0a735b901 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f198e19a1db870a5f7c8520f6b80f1d9e1cf74e864d0abeaeb843a9ccfd9b715 -size 37047 +oid sha256:998426a2ef42b3fcba4d833a94192ff84cdfca078e4e4beae76054677a517a20 +size 32722 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_3,NEXUS_5,1.0,en].png index dcdedbb448..7306748ca0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc3b73a8d026391bacbe66f774b2464a8455f35170bdd7e9316f06267b8913d7 -size 52410 +oid sha256:d21e45a40de75a8ffc30e574f3f9a1f308616c5b9bf25de36d5277db5911c373 +size 52396 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_4,NEXUS_5,1.0,en].png index b10f766e50..429e87b82f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c2f8f387a3d0998e73df5cc580c2199d6591c46aa86a87cc60c5659d9b5dee1 -size 50664 +oid sha256:4bf9b92e7f604738b91e09975c5ebfaee171a1c5aee7dce9f1b0a21ddac5cbc4 +size 50201 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_5,NEXUS_5,1.0,en].png index a25e7ff0fb..7d584650e5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d88db5cc763396241af1692e70254e3114aeb090bd9489e16b42ba2cc313e710 -size 47505 +oid sha256:fda8d3e0eb21cb9754f93699569acbfc79de6c5ba187528601342a0311f20aa1 +size 47079 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_6,NEXUS_5,1.0,en].png index a25e7ff0fb..7d584650e5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d88db5cc763396241af1692e70254e3114aeb090bd9489e16b42ba2cc313e710 -size 47505 +oid sha256:fda8d3e0eb21cb9754f93699569acbfc79de6c5ba187528601342a0311f20aa1 +size 47079 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_7,NEXUS_5,1.0,en].png index dcdedbb448..7306748ca0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc3b73a8d026391bacbe66f774b2464a8455f35170bdd7e9316f06267b8913d7 -size 52410 +oid sha256:d21e45a40de75a8ffc30e574f3f9a1f308616c5b9bf25de36d5277db5911c373 +size 52396 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_8,NEXUS_5,1.0,en].png index de7e05d8c2..534f9f2a3a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f694f450626abb04d70854a9c99849b2dc306d791914664a1f9856066eb400e -size 52228 +oid sha256:9992ccb1e020cb09e29c36285fc6acde477ccb2c53b5ab36d8fd6c19fbaf392c +size 52236 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_0,NEXUS_5,1.0,en].png index 807bbab00e..6bb74c767d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d23ee0e0ad834b6bb8f5e971c968a711952df38bcb310a05f201542940fcade -size 53595 +oid sha256:8a9cbc71ab3bc94c2bf38d89f453f9c14ed94fba96ab2c1cba5abe8169e4157c +size 53609 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_1,NEXUS_5,1.0,en].png index 6e124304fa..88c9263e98 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9a983309ba1ca5b121ba68b9bafa705ed55b01c2592b8e74c0127fc3d6aee21 -size 52169 +oid sha256:4f32621601d9acf2db4072cc990e655b2c6733a0429107e0c3525ceaa5ef18a4 +size 51222 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_2,NEXUS_5,1.0,en].png index e734739cd2..b1894d9d16 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74b6720ebdc8ad46b9ee0efcbe0042008567b1565ef43c939064a51212ba6cae -size 39206 +oid sha256:47df27bc44852fbf357b45b995365edc29d3d8ecebf1d5c93149416835b71b9a +size 34381 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_3,NEXUS_5,1.0,en].png index 807bbab00e..6bb74c767d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d23ee0e0ad834b6bb8f5e971c968a711952df38bcb310a05f201542940fcade -size 53595 +oid sha256:8a9cbc71ab3bc94c2bf38d89f453f9c14ed94fba96ab2c1cba5abe8169e4157c +size 53609 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_4,NEXUS_5,1.0,en].png index 467e43fed1..9832639e4c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a376e6d8424bd49bbeeda9608ba5bdc65e7a0b4fcaf6811725dce2dac9c56837 -size 52134 +oid sha256:2b59de4767530724d5d4bf6e38f6b198db7fe1c88863efaae28933536de6e13c +size 51552 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_5,NEXUS_5,1.0,en].png index 4ced14ce3b..37d9e41a98 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cf97bb9e0548a8a733a8dcd092364f3b17efdb9816a3cd44e1d0c96f7b36874c -size 48603 +oid sha256:29e8f90bfa5518b8ee35f88a9c40e3cfbd58b3b15e7cf4d94515a50a41ca98fe +size 48050 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_6,NEXUS_5,1.0,en].png index 4ced14ce3b..37d9e41a98 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cf97bb9e0548a8a733a8dcd092364f3b17efdb9816a3cd44e1d0c96f7b36874c -size 48603 +oid sha256:29e8f90bfa5518b8ee35f88a9c40e3cfbd58b3b15e7cf4d94515a50a41ca98fe +size 48050 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_7,NEXUS_5,1.0,en].png index 807bbab00e..6bb74c767d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d23ee0e0ad834b6bb8f5e971c968a711952df38bcb310a05f201542940fcade -size 53595 +oid sha256:8a9cbc71ab3bc94c2bf38d89f453f9c14ed94fba96ab2c1cba5abe8169e4157c +size 53609 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_8,NEXUS_5,1.0,en].png index 467668fa83..7bc1e268c6 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:24a8a645034c340794e1bf3e30168af8f1e1083a3cece9a3d7dfa3e4077d24ed -size 53365 +oid sha256:920cbdf29ae6d68182bf0e819878f894d6878f9b347bf6a3945c601ca696f0f8 +size 53391 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_ConfirmRecoveryKeyBanner_null_ConfirmRecoveryKeyBanner-Day-3_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_ConfirmRecoveryKeyBanner_null_ConfirmRecoveryKeyBanner-Day-3_3_null,NEXUS_5,1.0,en].png index 298e5bc5c7..858671e9a8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_ConfirmRecoveryKeyBanner_null_ConfirmRecoveryKeyBanner-Day-3_3_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_ConfirmRecoveryKeyBanner_null_ConfirmRecoveryKeyBanner-Day-3_3_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86af49a59603dfe9feddf14c84a738135f5a23e73e4c8f6d616b45f2643af3ee -size 29415 +oid sha256:6d745c94f892b5b7385ac1941f4fddfd34724f08fcd0606f09db47d0e702eab5 +size 29457 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_ConfirmRecoveryKeyBanner_null_ConfirmRecoveryKeyBanner-Night-3_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_ConfirmRecoveryKeyBanner_null_ConfirmRecoveryKeyBanner-Night-3_4_null,NEXUS_5,1.0,en].png index db7d9fd507..b593ee973f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_ConfirmRecoveryKeyBanner_null_ConfirmRecoveryKeyBanner-Night-3_4_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_ConfirmRecoveryKeyBanner_null_ConfirmRecoveryKeyBanner-Night-3_4_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c938be2898cd6eb51ab4bf2cbdeabaab3b89fda02b2328f8c149b6e6aa7f3242 -size 28840 +oid sha256:ad96b6285e61cedbb97672934f7185fb2fba29ca92ee6d9c70367588e8840032 +size 28874 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Day-6_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Day-6_6_null,NEXUS_5,1.0,en].png index 1ce758b42c..8c1b5bd263 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Day-6_6_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Day-6_6_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:773de163162ca8d1d38d65aea3dbcc145a29547643f756e6a6719f9910357036 -size 37286 +oid sha256:ce132903012c55bdca8fd9e081a4b92fbb9110b786bff2c15bed1f2bf81f6be7 +size 37003 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Night-6_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Night-6_7_null,NEXUS_5,1.0,en].png index 7798eb9edb..5aabd33a48 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Night-6_7_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Night-6_7_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c72953d38cc652d0c6c5737f6e115650855cf295505fe38adab5932a542b480 -size 42650 +oid sha256:66756228647566a3985f80c6ab362713abb8b6a18fd6eb45b5def593c49ad26e +size 42374 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Day-5_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Day-5_5_null,NEXUS_5,1.0,en].png index 7a4021f41f..7744a2ae25 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Day-5_5_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Day-5_5_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ea39cc532f9e523365e08d836a11d3aef4ffc8a653a95949be6c595c3f8711a -size 36845 +oid sha256:66a7de6dfd16f66a345ee092760f4e43db0099519584895bc5a73144001e6521 +size 36584 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Night-5_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Night-5_6_null,NEXUS_5,1.0,en].png index 0450054b40..9dd8019b48 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Night-5_6_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Night-5_6_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30c7cc4bc2c7abe40766edeaac6cd82f0c260f7135cc9964313ccc159011dc6d -size 42280 +oid sha256:e2f4761d2196058c740f051c457bb59cbbe000e06d2a0cf2c1b1c3db6e88a5c5 +size 42007 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RequestVerificationHeader_null_RequestVerificationHeader-Day-4_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RequestVerificationHeader_null_RequestVerificationHeader-Day-4_4_null,NEXUS_5,1.0,en].png index db9a827165..4a9c605eeb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RequestVerificationHeader_null_RequestVerificationHeader-Day-4_4_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RequestVerificationHeader_null_RequestVerificationHeader-Day-4_4_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dca845bd952e92a37f50aced5dd4d1dbe7dcaeccecade64cd1164a20efc65200 -size 26875 +oid sha256:f50a440f4604af5ecab18e86982ed919d62337a1b7ea339b8da7b78e63bf8be7 +size 26926 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RequestVerificationHeader_null_RequestVerificationHeader-Night-4_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RequestVerificationHeader_null_RequestVerificationHeader-Night-4_5_null,NEXUS_5,1.0,en].png index d11cf5060a..070d009558 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RequestVerificationHeader_null_RequestVerificationHeader-Night-4_5_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RequestVerificationHeader_null_RequestVerificationHeader-Night-4_5_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9c45cd3b849a04aba6813003632b8706d0e433ad6178b7eec616647f1b950ba -size 26039 +oid sha256:756253baaedf0a151bcc71e1929f769d9c621be01e2369469dd09389dd334319 +size 26081 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_2,NEXUS_5,1.0,en].png index 598c441d0d..35364ebadc 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c43973e2ea3e2f58eb8c4ae375ed503493bc4d1f739a69fcb6bd0db150b1852d -size 12658 +oid sha256:5315627c3cae4b07d3df50054d9a689148517b8fd0a0e88b160ff3f447513999 +size 12653 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_3,NEXUS_5,1.0,en].png index ccc6a19f74..ac31b4c341 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:14e5fcabf19783912bb03af5cde9df47eab7b6e22d6c9184f3513a8ab365507d -size 13527 +oid sha256:28b9ccdc834d9643694f72ce1c01fb988685a7b0300912bada032be1e426b467 +size 13539 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_4,NEXUS_5,1.0,en].png index 3adbb062f0..961345501b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:387f4543d02524e71992346335bd85c7fc457408dbe5cb23f8a1d4492dc0aeba -size 13280 +oid sha256:3ada109a4f7b3b3bfae402eaddcdc240fd5f08aad41e7f1e70878baa8e257f2b +size 13288 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_5,NEXUS_5,1.0,en].png index f9a82984a1..15fba97e39 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0f00ce6af3af7f2df1ee1ef9f953efb78347368824ae0b7daef2edbb154ef00 -size 13281 +oid sha256:0b18e5991be70ab16f983b929d08e566c85483e6d79f4de7a4191a800f4cae35 +size 13305 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_7,NEXUS_5,1.0,en].png index ef69c52551..547657fe23 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce2efae39dce136aa480a52ece41871dbdff32cc9358cf973698db25720a8ef1 -size 22205 +oid sha256:950e80e7e4be904967d35aaffe196548fa4f5ae9afdf2b86e65611ccdb14bf80 +size 22203 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_8,NEXUS_5,1.0,en].png index 66bf2311d0..b1778019d8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c554064b9bc895d5f9bb76c25166bdbe5b217b40ab5ece5d4b79c9d0144226e3 +oid sha256:004de09febcc9962bdc710a258f82083ba4ebca1c2831da48db7a1c9038d2caf size 12154 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_2,NEXUS_5,1.0,en].png index d169c59700..d5bf02373d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa160a5d77537b771e0f806821f0c0644c79235f3059ac57fed8ee04080a5098 +oid sha256:e86ea02d990c5db670a6aa3d9c17be62dad1192d687c1dc27177758b55cece3c size 12503 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_3,NEXUS_5,1.0,en].png index 90537c126f..f8e1416ce2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6495acf5edc645ad532334bf6f90cd07a7580d38057c42fe6922a1571d0c1a08 -size 13318 +oid sha256:584487de266a9b3e9cdefee0378023a946b309e19b09acffc5bf1a08dfc0d390 +size 13335 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_4,NEXUS_5,1.0,en].png index cc9127f0ad..11cdbfc582 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6054256c5f6791485c9505fe7354a01659c4552908c639aa5f6abd17fe8adcec -size 13101 +oid sha256:8bda8b4f6a84b37548e3926f180ddb282d220b8ff581a60e69e0e309ad3ae06a +size 13119 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_5,NEXUS_5,1.0,en].png index a6272371c2..057f4f914c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:33f1607f57f91bf4fb8a10c908af98778a1918e35afbcc94ca047e5a0fafa138 -size 12972 +oid sha256:2c17e87f046bbdd7a0300262737508cd1ffab4b54da9df7e7a7803237e070243 +size 13032 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_7,NEXUS_5,1.0,en].png index e8dd3d5b41..58ef0ec934 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec44c36dea5b03ed4b088d2fcf69a99c7062876c543fa9799c29e94019a3c839 -size 21327 +oid sha256:06df893c35c81609b46317f4de733db0bed4f5888eac189d09078ad7bd411cf0 +size 21340 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_8,NEXUS_5,1.0,en].png index f7fdf9be71..f2441b5999 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6975fc9b40a77082d4797a1493047163759fcfac6d6c59b389a57c5a06dcb5e9 -size 12055 +oid sha256:53248c6c1d0a05b8fd08bcd35a43a6463b03b9ad808b24f59d1fa7fe5d444d4f +size 12051 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-9_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-9_9_null,NEXUS_5,1.0,en].png index d95e361124..95f61ecfa8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-9_9_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-9_9_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:122b0b090fcb1bba3669daa6e56077305ea542d15ae784addf57f30924d9be8d -size 29956 +oid sha256:95659c6503ff1def5f3ec59bf96e9b47ea8f539ba90016c06aee3c1ead4d4387 +size 30047 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-9_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-9_10_null,NEXUS_5,1.0,en].png index cb209e52b7..32e13103bc 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-9_10_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-9_10_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69bfa6cbde376e66c4f2f93daac0e51b80dc60c5df557230c8396b74ef8235ed -size 29792 +oid sha256:56c5abd33920c390237f2889fceb20f18283c10fd075612ff7abc85bf6e5da52 +size 29887 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Day-1_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Day-1_1_null,NEXUS_5,1.0,en].png index b7dc6f82be..6e7ba7e325 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Day-1_1_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Day-1_1_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:509e303b4e62632032881ec1fc3531ff5113e774316d80a1a76e3b04fd30506b -size 12555 +oid sha256:5c7e78d4f8fd595922f4bdce5263bf9c5cf7094df5edb9a0e9d35fe270b21d23 +size 12541 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Night-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Night-1_2_null,NEXUS_5,1.0,en].png index d4b8e92df4..a6395dcbd7 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Night-1_2_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Night-1_2_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76cd5aa507bcbcdba1ee3de894d2e79f4caa1831345c84d8458b46d358ac3da2 -size 11891 +oid sha256:715154a104ef257a1865be35775cc7223e7adf3494863e0d24f1f1a8c7129920 +size 11888 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_0,NEXUS_5,1.0,en].png index 48b4f45709..3c9834d32d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c80b1212608cbc6fb00dcc5648e94df163eb02c01b98087a848e1d404af704a -size 65118 +oid sha256:f6415c4b16fca9f9c33fa26c320eba7db63414f9e7f80e4be8c06aadf0d761ef +size 64895 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_1,NEXUS_5,1.0,en].png index 6267da6f44..f0cdb4dc98 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:65a353822daf6999f79499bc9e0c7038ed89b8bb28772a7c1ff487f338cf8e9d -size 86560 +oid sha256:eb5cd4ad10a617fdc031c417402c20b8c83c9224ee4e4c40a0803d7399d1d338 +size 86439 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_2,NEXUS_5,1.0,en].png index 48b4f45709..3c9834d32d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c80b1212608cbc6fb00dcc5648e94df163eb02c01b98087a848e1d404af704a -size 65118 +oid sha256:f6415c4b16fca9f9c33fa26c320eba7db63414f9e7f80e4be8c06aadf0d761ef +size 64895 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_3,NEXUS_5,1.0,en].png index 4f1c518289..b9944b4d34 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:93f7207d43babcf2810063e60c61a913507fadcb5d8597f019136894e01f9335 -size 65094 +oid sha256:9482d5bdce23a63c4b682fc7ef6c48d3b29c42ae88eb22c0c79876591816ca1c +size 64880 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_4,NEXUS_5,1.0,en].png index 4c49e778c2..9b6f977aa0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f4457ac25047e469886b9fd2d547a87160bda23a0b398ecd4214e5426ebf36bf -size 66167 +oid sha256:7164dd963d88f12cd5b784d2184e9832b21f4465e42b1b8e687cdfde87e0c853 +size 65990 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_5,NEXUS_5,1.0,en].png index b7fd617cab..97a6d22a8d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:92a1f12cabc596dfa168a1746e1c8d0d1ba728c815fcb471bc1aa0fec9cee494 -size 66554 +oid sha256:01a3240614334fcacc98cdfe4fcba8d91629f000f6224cda2d2218c8d9fdb7aa +size 66366 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_6,NEXUS_5,1.0,en].png index cec02adb7a..085b79a6ed 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dcb426273cb249eeb92d60733ede317bc211177fe55915a6b8cd64a871628e8c -size 4913 +oid sha256:0d30d6d02466da97dfd4687c175e124c18f7a4fd113700fbd603781b72f07422 +size 4923 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_7,NEXUS_5,1.0,en].png index d95e361124..95f61ecfa8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:122b0b090fcb1bba3669daa6e56077305ea542d15ae784addf57f30924d9be8d -size 29956 +oid sha256:95659c6503ff1def5f3ec59bf96e9b47ea8f539ba90016c06aee3c1ead4d4387 +size 30047 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_9,NEXUS_5,1.0,en].png index 329925b0bf..25388c3a96 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d4e0b8856d2a08170bc89e9ce9d8f53575a8e79ceac13625213e60ab9d7718c -size 89753 +oid sha256:ff9327bbe5d92c91bfbe23811fa4776b4fc3c73ab26563a303e27cba4056e5e1 +size 89621 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_0,NEXUS_5,1.0,en].png index 5e0123fed5..95e4c489a9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3aba116389050f62ac9d61ae41599af35323fdf39a01a61a5bcb60a7ac5d9259 -size 67342 +oid sha256:47fce43a17dc5db55f901d43fe2ce6436ad18373129f0ca82ea3aecc07707442 +size 67127 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_1,NEXUS_5,1.0,en].png index 240ec106f0..194ad7e5cf 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9826ad9c429c5a6ece2ea2e5fa6fd1e602bb1453ce44ddcb9d952d33ec632729 -size 88498 +oid sha256:2d3228cbd10bee0e42b4d6ac09261b6b3d9eb6822f4d6fb8097fbe7ba06b2d8a +size 88321 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_2,NEXUS_5,1.0,en].png index 5e0123fed5..95e4c489a9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3aba116389050f62ac9d61ae41599af35323fdf39a01a61a5bcb60a7ac5d9259 -size 67342 +oid sha256:47fce43a17dc5db55f901d43fe2ce6436ad18373129f0ca82ea3aecc07707442 +size 67127 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_3,NEXUS_5,1.0,en].png index b81afb5e81..d4a3208718 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa09f985e9459b21d0663e3f1ffddbb57c7b47ce6255ad5bc10a285cbf55dbb2 -size 67122 +oid sha256:5871901c9988ce8954758e4b6487f8448b11e9f24984bae8bc4a05336fce99e6 +size 66896 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_4,NEXUS_5,1.0,en].png index f595818dc6..b7d2192eb8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d6b8d451c99fc0ed6e76b6bdfb809a409d975ef636500493f8021c1d2712b3fc -size 68942 +oid sha256:1bea5791ddb9180750462f17e65733a91bb246c6095f3b87477d3f0406d5a800 +size 68716 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_5,NEXUS_5,1.0,en].png index df4b0812c3..a44c83e6b1 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:593f2f027fb111f205dd740c8ab193e10c2b8096fbf49181b5c066c4cd58d299 -size 69301 +oid sha256:45fdc87068b3eec07720f4d81ed31c7914e3b6ba781dd5bf699a889a859218b1 +size 69069 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_6,NEXUS_5,1.0,en].png index 64886ad79c..b1bfe99a79 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f24387fc575a3c154b6f3dfa3590b175e4d8878e576f8dbafbc7b48676e86af -size 4865 +oid sha256:201687a0c3d91e939e0b951f10b35008675fca44195dd058484984dea7e911ba +size 4875 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_7,NEXUS_5,1.0,en].png index cb209e52b7..32e13103bc 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69bfa6cbde376e66c4f2f93daac0e51b80dc60c5df557230c8396b74ef8235ed -size 29792 +oid sha256:56c5abd33920c390237f2889fceb20f18283c10fd075612ff7abc85bf6e5da52 +size 29887 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_9,NEXUS_5,1.0,en].png index 134a0ebd5e..bc6be07737 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72d91b7b1b89f886c23123db3e297d84df46fc919bb60a4f8caa461b14d02f46 -size 91388 +oid sha256:c59880d152c30171ef28b06035067bb8209028eaf66ff77dc0492112a00979e0 +size 91200 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_0_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index b9e83ad531..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_0_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:83e046cae9a59996697172a964c55c53a35ab22404b46cb2fdbf45956254d36d -size 59003 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_0_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index ffbaec9732..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_0_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:576dc46e025bfa281a8f160315c7df052f8e9de85d1894a4a6b630eb10b4fad6 -size 47781 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_0_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index eb6a8476b9..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_0_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4335f4f058d36b6cf9364ee2135fe692783c3a73302ad7267599ca0d62f20fd5 -size 59675 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_0_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 7778573b62..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_0_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4ec952a7c1f8e032689bd045e7bf094987ef75e7ae63357ba247fbce087ff843 -size 30449 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f6271e8460 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:76e7306898cbc637a811110c7dd0ae6dece0842755d3f9c3a90fd0c7c07f9b2e +size 59005 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d059a1163e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f3148e7abdbf0b6d27d3fbebc9c237ecaa30a7724d35bd0421c89198b5e6165b +size 47784 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8491fdc798 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cd12f81dfc5856c5db3ae9421e05d38328ffc977aa9a778d0b4202d0e79b447e +size 59677 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..178750d1cb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e4b21aaf3ff6f327041bcf169ba4d493c24f18d1b2f8fbafa20a20c19e78561 +size 30447 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_1_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 8250f88fc1..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_1_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:17747da89c21acc64c3b869d1aaea3ce6308139e8e505a973e6190cd2b48e6ed -size 57249 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_1_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 7f195d3ca3..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_1_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b5c652cb90b4e8d5849270ba732417c0dc9cbe5e1f84dcf6916dfad43f712dff -size 41926 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_1_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index dce81936c9..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_1_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5e68b8e510524f04f4f764e0fd33b2b0b3e395bd87eeae0e467007bf60ab2708 -size 57739 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_1_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index baa10cfe76..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_1_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4e3b46d43c8a9ce48c7226fc3159d1ec72fd55cbf7dd5b4de866fb4081e52116 -size 26804 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b41c7b1376 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd7d552ceb9b86f4bacbd9bd0e20b7e895d29e17b113230e4de63a9680ced233 +size 57250 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..88031ebef1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:87be047661972a3fa72e069e79028cb64b4663c1188eb747593427209edcf924 +size 41941 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e4c1b50917 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e35b127fc263420421bb48153aa7fad1a65fca043bfd5756c92988ed3b356302 +size 57740 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b9a3c9c104 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9ea77618be64e5cc97ccd0b80e9524b8653d5539c5e8fa0db265e202dbfd7802 +size 26831 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_1_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 83407308e4..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_1_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8a86b726999a4d994fa4f7a778e4f80b122e912c5c15285ea91fd8814d8eb743 -size 15694 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_1_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 77cc2a53d8..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_1_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5778ff8908d601c853a0eef84ce72026baa7ca461b8dc5b2c30e48c95153bc91 -size 16278 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_1_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 82499e566d..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_1_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e4b919c589d65d61c7ee11588ff628e02603cd3e4c5fc79a672d3991d64fcebd -size 22276 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..feb7987b41 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:849fa392721e9b4e2232a704f45cc6163e6357651d1069fb1ab2345440aac55c +size 15696 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..77982cc44f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1c1f211e73d9de972ab992a2177af2f004fd3a3372f935cf1a88a91fa3fd55d +size 16281 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7be35c9986 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63678a4962dba8131e64b6951dfe93ca84785b245a2540ad83f46ad18233d0db +size 22278 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_2_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 435255d5cf..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_2_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:01653b0165eaf33b77948e41899cb21177fa70d9101eb04ad43ee7c7f4ab3607 -size 14718 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_2_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 91a649400a..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_2_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:39d22d5c0e0b88869900ddf28de055188c17c3e59dfa528a2d81b552ebb140a3 -size 15282 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_2_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 80e6693690..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_2_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2d4856648cb21a768d174fe1ab6675be9c1c565e1d535cd3fe7d92d3f6c05d1c -size 18970 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..77fa9498f2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9cebf1580d95f89cf875c9c2787798107e8c932eaa990c9ef1d423188faacdb +size 14723 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..44ac77db85 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:03ede18be1caac331973d48fd06c08c2914effc5aa6a20780b5606c59a5f2b6d +size 15286 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f74055c74b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7eeb470b7327e6a662c9057858114f343410fe49e48c0cbcaf809c0e0d2b1681 +size 18985 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_2_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index f3e629a2d4..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_2_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:05af89ec739d652391d64c56db2d5704075c34b8392386bfa6ca819b863b2fa0 -size 34283 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_2_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 35af4c7bed..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_2_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:829846411a53193e392dd82a024a835489064b495095a00967ec96e943ccc567 -size 46323 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_2_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 20d6946c75..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_2_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b5b01a97aec276f26df21b6c6d83968f330d89fd8c7e7ae42c79d88a67a7f705 -size 44649 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_2_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 06693a1a68..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_2_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2c373271f4e1e2cdfef322b67e4995632d94144a1cd4d61f9be05429f70eebfa -size 43704 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f676e5c07b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:66257f3f4d7d8178574b8b4deab2c7142f6c29aeb99c83930d5799f049dfc505 +size 34289 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b1b1f187c6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3db584d82b7d55dc3bfa938ebad7be835fb6f06f7475961db060eb8cc9b98405 +size 46326 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..99e28a0f87 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:23856a19db311a1b829dd05965154033cc4a7b4b143b73fde0cf7544bdaad9e2 +size 44649 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..1f4478dff8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:619bc51f59ea9011a23ad43a261ae73c0c1e09b476a67e3bdca3d9ec3f1a692e +size 43706 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_3_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 757c4ac571..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_3_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cad8f3834b84f1374fce154a90acc0b6d2279ecca29630575764962ef8a107e9 -size 32510 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_3_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 30232f185f..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_3_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d8725f375d5740d2b494b8ec2ce12e063a173f09de947c41989ae9d2311acfb5 -size 42987 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_3_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index c4411afea3..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_3_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3bbaf4d274fa3a52b96d6ac9daaf81843e3b2b3abfa7ee64d54df9135a6d35c8 -size 41559 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_3_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 7fec544d14..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_3_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:16be10f3495751a5324d4c6190454a76d6534e1af8faf293ead288e331fef0ac -size 38627 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..37728629fd --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc6c5fe6048fe1bcd5ee4c3a00561a705b3011b17c92cd8594f3a93f5d317533 +size 32511 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..fb9bdcada0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e03b605d6dfabab4add9502e4e58185442d21f00f36f6ccb9f34dc49c9c98e03 +size 42992 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..db94222ed0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9c055595664b4188a8098cfc999d4172091f046878106f6c1ce858eca46fc9f3 +size 41562 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..de6d3a8cb2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12aa860192cf02e19dfba9dd8060e96568b23766dc5ef5db30f05d7eb19c403b +size 38641 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index a6ce94832e..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fafe906b308c604f4ef98dab36b55b84fd235598aea738913301940bf4222a93 -size 41473 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 0abdbfd76d..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:92ce51c885eb9c5efb80b747ef107e185114401d235bfd261ec06a1bb9f53f85 -size 42518 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 0a55353c4c..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3c830fe829915419ea9917b201a80ab13a40c2c37783ac0e8b52bfcc5d0f8ae9 -size 42461 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index c6db44bf6f..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7f395fed6dfabb7f26a10f851e0fca7ea475722e1def7159f36d05cc9e47e09a -size 47990 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index 0abdbfd76d..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:92ce51c885eb9c5efb80b747ef107e185114401d235bfd261ec06a1bb9f53f85 -size 42518 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_5,NEXUS_5,1.0,en].png deleted file mode 100644 index a6ce94832e..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_5,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fafe906b308c604f4ef98dab36b55b84fd235598aea738913301940bf4222a93 -size 41473 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_6,NEXUS_5,1.0,en].png deleted file mode 100644 index 8e822c7ef2..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_6,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3747da81312e3f34da13e6bd530301550c7307856cc7a31aa009be2d60002ecc -size 26338 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_7,NEXUS_5,1.0,en].png deleted file mode 100644 index a6ce94832e..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_7,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fafe906b308c604f4ef98dab36b55b84fd235598aea738913301940bf4222a93 -size 41473 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_8,NEXUS_5,1.0,en].png deleted file mode 100644 index 557ce7e1ce..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_8,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:083440e0fa8699c8fd370bb13351d04c01fa2c78c273f8e2b07bff0b09e4f2f1 -size 31938 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..35f3978407 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:157d7e71b29ed9fcf5b9d7169820183f947524ee109fd9c7de73d356405cb1a5 +size 41472 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..47faa33827 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6dfa6240961eaf11431c3c4e513fbfb68e543f2ee1fe6d8875c24689f068097f +size 42515 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ff7033e8b6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:45f9fe3bf1e558169b8c79703b168b250ffd2b4eadd707adc1282781d3ec7676 +size 42459 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4df26e43d7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6dd323965929e2da99f8f0c2a33db09ecfd89804fc0f09a9852ee70ae5047cb +size 47989 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..47faa33827 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6dfa6240961eaf11431c3c4e513fbfb68e543f2ee1fe6d8875c24689f068097f +size 42515 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..35f3978407 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:157d7e71b29ed9fcf5b9d7169820183f947524ee109fd9c7de73d356405cb1a5 +size 41472 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b3ff9c85af --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c06dfd2c060f6e94dde6b5eabe3f3e64fd36246c5c8b4f2c4472b4e27084bd0 +size 26338 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..35f3978407 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:157d7e71b29ed9fcf5b9d7169820183f947524ee109fd9c7de73d356405cb1a5 +size 41472 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..14f108d258 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ed7f0090053318b0051e3cbe8a42a356a6019659460e0ddbd9dbcd7d3b7f397 +size 31937 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index be5ffbb21e..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:94869b0aaccbbc1e82fb96de5f71fa7e9a59ba36317213c0f9a6bb4f9448e2b1 -size 39451 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 3a7029466e..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:19965ae446d383e69f8f05dc3e3cb1d3956a9962c472d9246b765ae467ef8d56 -size 40318 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index d8b61cb909..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:89e3cf46598c82bc2957ba497a4018654067b157e9da23b23240213c152a5b8e -size 40163 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index cbfa69161d..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0a8411a6c7df7bdc79a526755745fd2b2fed1c32676acd9c9b1135b2391e5abe -size 45326 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index 3a7029466e..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:19965ae446d383e69f8f05dc3e3cb1d3956a9962c472d9246b765ae467ef8d56 -size 40318 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_5,NEXUS_5,1.0,en].png deleted file mode 100644 index be5ffbb21e..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_5,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:94869b0aaccbbc1e82fb96de5f71fa7e9a59ba36317213c0f9a6bb4f9448e2b1 -size 39451 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_6,NEXUS_5,1.0,en].png deleted file mode 100644 index 4673753cdc..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_6,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:14cb5e199172c0b325175e913708fb88111d479af06c1ac2c380a8cd68a80021 -size 25090 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_7,NEXUS_5,1.0,en].png deleted file mode 100644 index be5ffbb21e..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_7,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:94869b0aaccbbc1e82fb96de5f71fa7e9a59ba36317213c0f9a6bb4f9448e2b1 -size 39451 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_8,NEXUS_5,1.0,en].png deleted file mode 100644 index f9da40e05d..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_8,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:59912e13e6cfec021b7c12d689029f159a0b69e91905323f710d47124e67a7ea -size 30051 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b56733b351 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75372f01fcd2cbad5802a505c1150ec6e14484cf89ab700e6ec2e5c5f9e16a8a +size 39453 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c4da1a4d5b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:630e944f09f38e1b049f4ba4615c2c39e7d2617a9358b934960201809c820e0c +size 40320 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..758e288be1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6d1267540818bc469a9531e013868b3e8ef92bca6ac9811b68697c694dd93df +size 40166 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7eb2c617d0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8dc4996be5af5a42d91e9f0fb371c821247264d980bde56e870441750fb439e0 +size 45328 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c4da1a4d5b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:630e944f09f38e1b049f4ba4615c2c39e7d2617a9358b934960201809c820e0c +size 40320 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b56733b351 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75372f01fcd2cbad5802a505c1150ec6e14484cf89ab700e6ec2e5c5f9e16a8a +size 39453 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ad47b678e1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb6d0f7866767b529ed58e642d45cde89da0535b2d024921dde19d47808206a2 +size 25094 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b56733b351 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75372f01fcd2cbad5802a505c1150ec6e14484cf89ab700e6ec2e5c5f9e16a8a +size 39453 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..88c330d340 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4fd29e5953cd9782b7748beae7078011147129cecfcadadd7fc04533f7fdd3f5 +size 30053 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 1c2e58a7cf..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:eca49fe4bdc67ff1ac9b10da19feab91a0efc9705c82b772e15909775765a907 -size 21961 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index adc3eae19e..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:70fe9112996b3d463283bb51cd30cc5bac92ecc2471a63f8c97808462fd4f299 -size 19819 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index b9d7e00faa..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a01cd38fa99db8e54327c35d1b157a24ec2a100c206ddc45f54f5634d23bf67d -size 22141 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_5,NEXUS_5,1.0,en].png deleted file mode 100644 index adc3eae19e..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_5,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:70fe9112996b3d463283bb51cd30cc5bac92ecc2471a63f8c97808462fd4f299 -size 19819 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f711d71eba --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:19415aa829e2bc94964ee169111f5872f5d8946452b4dc5d67f7edcd6ede3647 +size 21959 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..82de396351 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac24f1cdd412b7b2c08e34b735b700acd5f122b12c3f98be9ab218d117f19805 +size 19819 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_10,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_10,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_10,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_11,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_11,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_11,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..9ae3711843 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eec81240ce24957a810623d7e4790f8cf7b07ca1ee6cc307ff7cd391a725036d +size 22142 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..82de396351 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac24f1cdd412b7b2c08e34b735b700acd5f122b12c3f98be9ab218d117f19805 +size 19819 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_6,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_6,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_6,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_7,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_7,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_7,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_8,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_8,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_8,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_9,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_6_null_9,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Day-6_7_null_9,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index c319814f17..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:721f24dec2187a504722f9e055c364b1157126d48014e0f88172593ccdbc9e56 -size 20931 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 45d5e54d51..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:07478df93245af67fa863480313facd7b4d2923f284446b55f956e45ec5fb2f1 -size 18905 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index 97c0b0cc9d..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:97dfb9633ae86b94e95d5db567400e7ac84f25a8b66705dea6d623437434d329 -size 21136 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_5,NEXUS_5,1.0,en].png deleted file mode 100644 index 45d5e54d51..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_5,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:07478df93245af67fa863480313facd7b4d2923f284446b55f956e45ec5fb2f1 -size 18905 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..57ac4264f2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d84fca4be619f8e9a381a6dedd0801f04e307224613cae7a23f6a2fae9352710 +size 20942 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..cffda6fffc --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bce3d3a66bb99ab71aaaacb2481663e78b35a62ce50172dbbadd982b4a97039b +size 18913 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_10,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_10,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_10,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_11,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_11,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_11,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..00cd02aaa0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ecbdd39730720020e7c2b68b11edb22e4c5cabe0fa03ae2913430bd3e0bfee37 +size 21136 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..cffda6fffc --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bce3d3a66bb99ab71aaaacb2481663e78b35a62ce50172dbbadd982b4a97039b +size 18913 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_6,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_6,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_6,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_7,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_7,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_7,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_8,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_8,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_8,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_9,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_7_null_9,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup.views_RecoveryKeyView_null_RecoveryKeyView-Night-6_8_null_9,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_5_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 23bb8e9846..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_5_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dbe8d4ff5c2fca528c12e2475ec2179489c6cbaf3e98d0fa45f0980dcd4f9aab -size 50590 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_5_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 1099362a48..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_5_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f1950689df7774679f0a10f679fc5be61917d3f89fe127dff037c119d675a0f7 -size 48482 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_5_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index a7fe29f246..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_5_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:744f0f6ec4910b54fd13ff423b1e0555ac0cff1c59123e6f5d096496f14603af -size 53238 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_5_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_5_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index a7fe29f246..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_5_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:744f0f6ec4910b54fd13ff423b1e0555ac0cff1c59123e6f5d096496f14603af -size 53238 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_5_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_5_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index 42f54c2b23..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_5_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5d958c172b24915c6b03d8e75ca2611f95cf523eae30cdfb0af4bc40e27cb6c5 -size 48553 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..bc2ae46f25 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ce5bf5f221e71ef09f2467e160297db6a538ecaec7f423451edb521b3d743725 +size 50588 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..0cfd15d94e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:934e4749c80b61c3b4b1fcfb7573ba12186f31260198c3b16595b5b23ab17fc9 +size 48481 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4e5f1e7f9e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7872cba8e1bf900d81288748b0e55e945bb551525707f20fe0711931a483c22b +size 53234 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4e5f1e7f9e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7872cba8e1bf900d81288748b0e55e945bb551525707f20fe0711931a483c22b +size 53234 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a8ed14d18d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Day-5_6_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ef611386d451c8849c3c8cdcd2344d8c19aa66092cbdd52bea58a9cf882ab22a +size 48550 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_6_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index b84ddbac3c..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_6_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1c426686054a0b72361c9ec2b93318c91f01f0d365d03593b03596dcfeef162d -size 48559 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_6_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_6_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index e6c46c2e47..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_6_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1fca0245cec36a31642087fcafd4d65b1d670dfe315b6cadd0203be3e029984c -size 46307 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_6_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_6_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 46fcda17a4..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_6_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5b4290983b3a2b2f13df38f6d95c2ae777230aae7018209f807793186eb2565b -size 50426 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_6_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_6_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 46fcda17a4..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_6_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5b4290983b3a2b2f13df38f6d95c2ae777230aae7018209f807793186eb2565b -size 50426 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_6_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_6_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index ab08a14180..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_6_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:20530d92efe5c6c52daaa99342923ad7310f801515677899f77d1f5bf1de41f2 -size 42597 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6e8461a284 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0ba7c55493f8c25285889c9c8f4e12bb838dd7827697da967d03de723e4ef766 +size 48555 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c8314ad20c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f60f9eb53c57170e063b296d60308214d442bda3039d570e511860babc6fff38 +size 46302 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..3a647c4592 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c5f3cbe3e3158a6958e12a9848dda9070b70e14a27fb5872a8d173673b93aeea +size 50434 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..3a647c4592 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c5f3cbe3e3158a6958e12a9848dda9070b70e14a27fb5872a8d173673b93aeea +size 50434 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..50b7e8170b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupViewChange_null_SecureBackupSetupViewChange-Night-5_7_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c31a548db85548409db4071c9ae34411d6c93de7f23b514034c0a58679fdaa20 +size 42588 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_4_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 65ecaf38f8..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_4_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:09e3eb3b83d8f0b74e0d237b3424c68a9b90e077c37d2e472a194583086f7115 -size 51884 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_4_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index c3f68fac76..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_4_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a8cd8131baca93c8fb4c40cde91671155052cc749b0cefe4ba5a276f89f212ab -size 50017 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_4_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index a7fe29f246..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_4_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:744f0f6ec4910b54fd13ff423b1e0555ac0cff1c59123e6f5d096496f14603af -size 53238 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_4_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index a7fe29f246..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_4_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:744f0f6ec4910b54fd13ff423b1e0555ac0cff1c59123e6f5d096496f14603af -size 53238 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_4_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index 42f54c2b23..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_4_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5d958c172b24915c6b03d8e75ca2611f95cf523eae30cdfb0af4bc40e27cb6c5 -size 48553 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6f3d6c5ae5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f7cc54d19a41c6920c51d799f17ae5d2cf4d0bbeaf58d32c83dae52c24ac1f8 +size 51883 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2bd5b57a2b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3c73f15a0a31e0e6665fc3e278f64dad957d7f717afa4213094c88edbfdb728e +size 50018 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4e5f1e7f9e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7872cba8e1bf900d81288748b0e55e945bb551525707f20fe0711931a483c22b +size 53234 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4e5f1e7f9e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7872cba8e1bf900d81288748b0e55e945bb551525707f20fe0711931a483c22b +size 53234 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a8ed14d18d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Day-4_5_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ef611386d451c8849c3c8cdcd2344d8c19aa66092cbdd52bea58a9cf882ab22a +size 48550 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_5_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index cb2fa2402e..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_5_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9973919b98ce55f98cb9bc00b98af8b90b7b7355235cad6bc1b4b8af799f6bc3 -size 49981 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_5_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index c3f3f7c463..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_5_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ebc7011596661eafd90f646f59e986e7f46d942188fb0224fec03b8362284ab1 -size 47988 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_5_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 46fcda17a4..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_5_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5b4290983b3a2b2f13df38f6d95c2ae777230aae7018209f807793186eb2565b -size 50426 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_5_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_5_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 46fcda17a4..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_5_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5b4290983b3a2b2f13df38f6d95c2ae777230aae7018209f807793186eb2565b -size 50426 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_5_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_5_null_4,NEXUS_5,1.0,en].png deleted file mode 100644 index ab08a14180..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_5_null_4,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:20530d92efe5c6c52daaa99342923ad7310f801515677899f77d1f5bf1de41f2 -size 42597 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..1ac830b344 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:757ed00bc9ff04e2a4142daf475acb3ba28d1441765c1a8b322fa9328bb62676 +size 49979 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..67cb5d4067 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15be1d03c579a5427493087f0097f76d03d6d36564d01cbd6d7149576e93f102 +size 47986 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..3a647c4592 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c5f3cbe3e3158a6958e12a9848dda9070b70e14a27fb5872a8d173673b93aeea +size 50434 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..3a647c4592 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c5f3cbe3e3158a6958e12a9848dda9070b70e14a27fb5872a8d173673b93aeea +size 50434 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..50b7e8170b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_SecureBackupSetupView_null_SecureBackupSetupView-Night-4_6_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c31a548db85548409db4071c9ae34411d6c93de7f23b514034c0a58679fdaa20 +size 42588 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Day-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Day-0_1_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Day-0_0_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Day-0_1_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Night-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Night-0_2_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Night-0_1_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Night-0_2_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl.emoji_SasEmojis_null_SasEmojis-Day-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl.emoji_SasEmojis_null_SasEmojis-Day-1_2_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..63acb4565c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl.emoji_SasEmojis_null_SasEmojis-Day-1_2_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0d2eb0d4631129167e2a2d0e88341723050fe737e9ef807de028177fadde63f +size 145991 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl.emoji_SasEmojis_null_SasEmojis-Night-1_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl.emoji_SasEmojis_null_SasEmojis-Night-1_3_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..487447301e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl.emoji_SasEmojis_null_SasEmojis-Night-1_3_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:259c86326608b0d9c3595c1fb53768322c9c43463f4ce7442ceb11e684115978 +size 143665 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_0_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index d42407f5bb..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_0_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b801d06d7ec8cab2a9a2804541cdf2e0e27dfed949bddb29b3ce7e6b348ac452 -size 56485 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_0_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 806c701471..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_0_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:41983afd517fc4a8ad8347ee1e07ce65a58ab4677dc2c07582aefef2764f1263 -size 57899 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_0_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_0_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..de0e46a071 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8425428904c58fd9a22f4505c7dbb47798efcf2f2e5007fbf3f402222cb0cf53 +size 51381 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..258a528e6c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5318f2a7564c319c58971daf76a60a696d0835476d06666a190849a7222c98d +size 52689 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_0_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_5,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_0_null_5,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_5,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..fb2edac72f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Day-0_1_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:22817cd770e9eb1d438128804007a33cc038f8da17788f0848ec5db26211a179 +size 35569 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_1_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 81d26981a8..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_1_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2d5701107cf5091ac15c167daa45e7eb38d36ee119eef31ffaaaa0e94da1c7d6 -size 55684 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_1_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 4f3e4a9556..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_1_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4c56c2c33e4b4c1e4175a2f9ea147680bd1b463a925cc62d2f4fc5a82d53bae9 -size 57085 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_1_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_1_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..1c24f49ca9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:77668eb95611c9b72913d286956b1b5e6bafe1cf6a1facfc00a5c4fd0ceab3d2 +size 49133 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..dd78bc04ea --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:121d1b8f9a6abf9c50a951b2db6a7e52602ec34876a55e38b009f1fdffcd725d +size 50525 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_1_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_5,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_1_null_5,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_5,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7fb3f28454 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_VerifySelfSessionView_null_VerifySelfSessionView-Night-0_2_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:83961b489de278edf3f2646f228e059e71f381363ffb751f44244a7d12604cb9 +size 33443 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_DialogLikeBannerMolecule_null_DialogLikeBannerMolecule-Day_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_DialogLikeBannerMolecule_null_DialogLikeBannerMolecule-Day_0_null,NEXUS_5,1.0,en].png index 9315297856..914f0082ee 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_DialogLikeBannerMolecule_null_DialogLikeBannerMolecule-Day_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_DialogLikeBannerMolecule_null_DialogLikeBannerMolecule-Day_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:45736b242f9bf1a50fb14af9b340633f0fdc348ed7a846c4c57c3a56a4b2a48c -size 11429 +oid sha256:b3c5ad600cac265ed3004a3bc84e44a415a5815cbf8c9b267243805f01532e68 +size 11502 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_DialogLikeBannerMolecule_null_DialogLikeBannerMolecule-Night_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_DialogLikeBannerMolecule_null_DialogLikeBannerMolecule-Night_1_null,NEXUS_5,1.0,en].png index 7defe86721..73ca661ca9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_DialogLikeBannerMolecule_null_DialogLikeBannerMolecule-Night_1_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_DialogLikeBannerMolecule_null_DialogLikeBannerMolecule-Night_1_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f9989dadb7c68cf705683997689f48595be9283eb939d934819399ec3c6805f -size 11209 +oid sha256:e0944422659a6398476566bd44ad9cc162681415cb2e140cccc67388f616993c +size 11262 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_IconTitleSubtitleMolecule_null_IconTitleSubtitleMolecule-Day_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_IconTitleSubtitleMolecule_null_IconTitleSubtitleMolecule-Day_0_null,NEXUS_5,1.0,en].png index 8fb30525d0..47992cca98 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_IconTitleSubtitleMolecule_null_IconTitleSubtitleMolecule-Day_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_IconTitleSubtitleMolecule_null_IconTitleSubtitleMolecule-Day_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b514737761239d60aac83a2b5d822c1079b7029474097f5ea4a4a29a88174e81 -size 10687 +oid sha256:6ce504b7951ee35496709f782dc240e05308dff6943a0e4f0881d2afbc47ed2d +size 10723 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_IconTitleSubtitleMolecule_null_IconTitleSubtitleMolecule-Night_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_IconTitleSubtitleMolecule_null_IconTitleSubtitleMolecule-Night_1_null,NEXUS_5,1.0,en].png index 95a1fb946e..8b555f4b61 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_IconTitleSubtitleMolecule_null_IconTitleSubtitleMolecule-Night_1_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_IconTitleSubtitleMolecule_null_IconTitleSubtitleMolecule-Night_1_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d648d16fa94fa4add5784cb0ca32173cc447dfd592bcf88ff82b53894c02f137 -size 10527 +oid sha256:3b31b7210ac744ae0dcb7e316110c6885347be3c96420404d25c2a8f28a614d2 +size 10554 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_InfoListItemMolecule_null_InfoListItemMolecule-Day_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_InfoListItemMolecule_null_InfoListItemMolecule-Day_0_null,NEXUS_5,1.0,en].png index 48421f17bf..32d96cfaf3 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_InfoListItemMolecule_null_InfoListItemMolecule-Day_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_InfoListItemMolecule_null_InfoListItemMolecule-Day_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cfaf25db5b5f53240416bc0c0bbebc5d1f3f95818c93ea47b67ed7d919e32ab5 -size 19527 +oid sha256:f011a920aad795bd2839c6bb1c191aa03dea02315b257e1957da5b711db2b582 +size 19496 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_InfoListItemMolecule_null_InfoListItemMolecule-Night_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_InfoListItemMolecule_null_InfoListItemMolecule-Night_1_null,NEXUS_5,1.0,en].png index 34e2bad93b..23a7c91ba8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_InfoListItemMolecule_null_InfoListItemMolecule-Night_1_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_InfoListItemMolecule_null_InfoListItemMolecule-Night_1_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:524df16c773bdd643389fc7de33247ed2e6c8d77e67c3119a9f438bf0b5b0fdd -size 18837 +oid sha256:46d222411ab4e170697cbcf12cb1bb03468fa34aaad63a3c2fae7b7513b9862f +size 18822 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_FlowStepPage_null_FlowStepPage-Day_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_FlowStepPage_null_FlowStepPage-Day_0_null,NEXUS_5,1.0,en].png index 94df7fad03..873acf46c5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_FlowStepPage_null_FlowStepPage-Day_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_FlowStepPage_null_FlowStepPage-Day_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:735eecae838f9ea7413d44e33d1ff7b7ce8524a7ee9c601d889f2a23cd06dbee +oid sha256:a4ef7f9c81eebb82a49506aaed97ab8418ee5be0a057d5f3f89629933bfa7e87 size 18530 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_FlowStepPage_null_FlowStepPage-Night_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_FlowStepPage_null_FlowStepPage-Night_1_null,NEXUS_5,1.0,en].png index f8b715c693..f8d86fe0f7 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_FlowStepPage_null_FlowStepPage-Night_1_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_FlowStepPage_null_FlowStepPage-Night_1_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e1ec13019a59463953135a6b469bcaeed3e61f868efd45c8f7b612a6d96ba711 -size 17672 +oid sha256:86acc0f7eb15bf5a90dd7fe886b5a67d2e4e2254e5c7834a2656d2537f24f941 +size 17688 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_10,NEXUS_5,1.0,en].png index 56cb2f3f4d..d52001cf81 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:385ccb31d2b5a286e2bc4db603d0332295fb6f31841e59440f042c99c5c3d5d5 -size 18837 +oid sha256:644e19416b18c16d3e7de1f81e040c0c63de69790e5ca5fb187cc2ee6fff0257 +size 20418 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_11,NEXUS_5,1.0,en].png index 59e69276a6..625943f333 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f6a1ccf2355fb763fee559815f576c185751a00e8560c3079fa7017d1c7d1cf -size 21920 +oid sha256:e81982a68990e5007a831c6809e186d7d384b037a8b2fdd0d7ee0de50dc33569 +size 23352 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_9,NEXUS_5,1.0,en].png index b886f9140e..dc6ff93e0f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:73356882450cd90a01b8056379848967d3bea1e640ee4e901d9d21a1e823054d -size 19753 +oid sha256:3256d894dc36551db660060607bdd8fca172aa1aa67939053b9c09b52ed997a2 +size 21282 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_BackButton_null_Buttons_BackButton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_BackButton_null_Buttons_BackButton_0_null,NEXUS_5,1.0,en].png index 6a9db51c01..817cfad89c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_BackButton_null_Buttons_BackButton_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_BackButton_null_Buttons_BackButton_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e46b9b820f7ff19ed00db0e786d50ee6c94d764112507d2ba0c5ee7312d4f86f -size 7634 +oid sha256:6b04109a794e5451046715eab68b83db3ccde79799497e315c057a87a901fe3a +size 7670 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_MainActionButton_null_Buttons_MainActionButton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_MainActionButton_null_Buttons_MainActionButton_0_null,NEXUS_5,1.0,en].png index 44b3ccdc7f..4038cc83e5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_MainActionButton_null_Buttons_MainActionButton_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_MainActionButton_null_Buttons_MainActionButton_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ef39621c2f80917dcce22382566345d84a93dd96cf8db0bad1b873874d267591 -size 15110 +oid sha256:f2bf4e7519e0fb7e829996095c894cc18230ab69686446713783163cc238264f +size 15113 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceCategory_null_Preferences_PreferenceCategory_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceCategory_null_Preferences_PreferenceCategory_0_null,NEXUS_5,1.0,en].png index f891a5843a..d001d3fab3 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceCategory_null_Preferences_PreferenceCategory_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceCategory_null_Preferences_PreferenceCategory_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d53b0847befd7041345248a6123de5dc8e8a1174ad473aaa9fc77a1e17c100c -size 30417 +oid sha256:bcbfb437e0ebdf0b79879f298d6838978101de90281e0cbfb211360bf3e0cdc5 +size 30421 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceCheckbox_null_Preferences_PreferenceCheckbox_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceCheckbox_null_Preferences_PreferenceCheckbox_0_null,NEXUS_5,1.0,en].png index 4a3139eb0d..e44f526645 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceCheckbox_null_Preferences_PreferenceCheckbox_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceCheckbox_null_Preferences_PreferenceCheckbox_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b29ba6e66834babaf615ecf7c384cc03bbee0a3373cfc478e3517819da0ad853 -size 28805 +oid sha256:ede8374ebed5546cfcfbf1a8706116904d717c2f45a9787551419766d93cd221 +size 28808 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceSlide_null_Preferences_PreferenceSlide_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceSlide_null_Preferences_PreferenceSlide_0_null,NEXUS_5,1.0,en].png index b3db45f136..298dfbe55e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceSlide_null_Preferences_PreferenceSlide_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceSlide_null_Preferences_PreferenceSlide_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8382aa61eddcbd357a269e83018ead4bd951f73a0e27eaa512a87f1c75f42c74 -size 15809 +oid sha256:0197fae68e35c3526fbf8416fdfb00a0c992097af386f7bcb589208bc6d5d32b +size 15803 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceSwitch_null_Preferences_PreferenceSwitch_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceSwitch_null_Preferences_PreferenceSwitch_0_null,NEXUS_5,1.0,en].png index 615066b23b..81237a4319 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceSwitch_null_Preferences_PreferenceSwitch_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceSwitch_null_Preferences_PreferenceSwitch_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0f7189265e954d287b19dfd023ced8229393f84fb4dd3a1e9eb269b0debe3bd -size 17874 +oid sha256:d0d43096586a1bf7daf093c8a7e6f921808b988bd391c74eca3a80e681da64ae +size 17879 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextDark_null_Preferences_PreferenceTextDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextDark_null_Preferences_PreferenceTextDark_0_null,NEXUS_5,1.0,en].png index d86d6ec555..23b57342f0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextDark_null_Preferences_PreferenceTextDark_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextDark_null_Preferences_PreferenceTextDark_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f03589a34474517da7faf7da9141ce59500be1c7169fe58dd5db0caafdae61ce -size 36339 +oid sha256:b2e8a2c21ac6ea8dbd92ba276ea8febc5f4c4b54b1282924a56a5ed76c8614c4 +size 36273 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextLight_null_Preferences_PreferenceTextLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextLight_null_Preferences_PreferenceTextLight_0_null,NEXUS_5,1.0,en].png index 9546ffdacf..a3398ee26c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextLight_null_Preferences_PreferenceTextLight_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextLight_null_Preferences_PreferenceTextLight_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2b6e813796bbce262c8028785d52fa126c8a47ffab46f9782ab4695ab504bef -size 37350 +oid sha256:66fc3f9fdb30be511d4a93492717e90810777d913c00f2f6a83bf22ea4b95952 +size 37360 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextWithEndBadgeDark_null_Preferences_PreferenceTextWithEndBadgeDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextWithEndBadgeDark_null_Preferences_PreferenceTextWithEndBadgeDark_0_null,NEXUS_5,1.0,en].png index af08aa21f2..f3318219b0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextWithEndBadgeDark_null_Preferences_PreferenceTextWithEndBadgeDark_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextWithEndBadgeDark_null_Preferences_PreferenceTextWithEndBadgeDark_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:59a025a97a33c8fc8b17e1b666df6c6eb114d5580ed919108d72cd514094e80a -size 38234 +oid sha256:ba0c401a799d1a71408ccc45f6a6ff93ee7be0e399445edf069dd043b8cfd8a5 +size 38186 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextWithEndBadgeLight_null_Preferences_PreferenceTextWithEndBadgeLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextWithEndBadgeLight_null_Preferences_PreferenceTextWithEndBadgeLight_0_null,NEXUS_5,1.0,en].png index f59111209b..16db8c3cfa 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextWithEndBadgeLight_null_Preferences_PreferenceTextWithEndBadgeLight_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextWithEndBadgeLight_null_Preferences_PreferenceTextWithEndBadgeLight_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f596886595b77bf358da5c74422249c4e27426b5455b0c16ba2974dc3e4da1e4 -size 39448 +oid sha256:5f90ee268d5393faa46a064c4fda59bfb9a7f728f72c6568b61326a1e02c5b3b +size 39438 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceView_null_PreferenceView-Day_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceView_null_PreferenceView-Day_0_null,NEXUS_5,1.0,en].png index a9a6095b7e..2ed17ccca6 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceView_null_PreferenceView-Day_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceView_null_PreferenceView-Day_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b22763db946d824e53440a1d65e6aaba8dac57edcb7e53bac1475fbac81b6feb -size 28567 +oid sha256:a96dbfcf93e25198c32bb954c609cb6d474fcab3dc7976f93af6c57d977c4660 +size 28553 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceView_null_PreferenceView-Night_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceView_null_PreferenceView-Night_1_null,NEXUS_5,1.0,en].png index cf02bbd741..cff0bab715 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceView_null_PreferenceView-Night_1_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceView_null_PreferenceView-Night_1_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9994aec4ebcbad6458a4bbd8cf741ece95916187c283be38ea56fecbfe82f75e -size 26566 +oid sha256:09561f9e5c44bd224fb482ae5bf55d5c108dbacd493c0c24068ed575e0245d2b +size 26560 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_Bloom_null_Bloom-Night_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_Bloom_null_Bloom-Night_1_null,NEXUS_5,1.0,en].png index 366b13e42d..c36c0d2a11 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_Bloom_null_Bloom-Night_1_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_Bloom_null_Bloom-Night_1_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2d4bd4cfa4d1556682f1b59b080d551ab8a11009755ed85181c00b32005c1ff -size 68722 +oid sha256:21ded0d42d131dd9bc51188fc4c6866130c06dc73e0c856d2f15fc82124983cc +size 68724 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_Bloom_null_Bloom_Bloom_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_Bloom_null_Bloom_Bloom_0_null,NEXUS_5,1.0,en].png index 021d782909..b06ba778b0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_Bloom_null_Bloom_Bloom_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_Bloom_null_Bloom_Bloom_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f81ce6f66e9d1054b6c272b64fa9c9859f4a21e9f94839f2a58fec49476868c8 -size 77558 +oid sha256:9e820d72e95e24db8072ef64a0860d40d15ca5f9bf4b7f8d8fd49c5600093bca +size 77531 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Day_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Day_0_null_0,NEXUS_5,1.0,en].png index 47c301100c..5e73c308b2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Day_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Day_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c10bbc922df738a2b45424142dd476c71c336ac398fa93be98a1c1a421d566a -size 71443 +oid sha256:f8047dcf8b8b514967d73d008cd24c0caa11a6f31a9a4a6bdb7d64e262f394e6 +size 80432 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Day_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Day_0_null_1,NEXUS_5,1.0,en].png index ed785a85c5..7462a51174 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Day_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Day_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66ce02b19066a21d1ebf1b2963872df484cdce5f4eac16ec0d7d215ba7276b9a -size 84144 +oid sha256:93259e689404ece68cc2445e2ff67469f42305ea368dbe09aac31ef4836ff8e9 +size 80199 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Day_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Day_0_null_2,NEXUS_5,1.0,en].png index 55fc22a44e..74293d6003 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Day_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Day_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15429b5c37ef7ebb125fc4c00741d5fb8a123ce6764d5eccf1697b6821bf6238 -size 18823 +oid sha256:74927b01779f3bb665ad770f37f026368044aecbd76b01ee0dc8b99ef0755da0 +size 92111 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Night_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Night_1_null_0,NEXUS_5,1.0,en].png index 32005147d7..9144999194 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Night_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Night_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a479cf4d877676d41df4e618571d8d51e44fe42dbb3e49d3db0f31cf0500928a -size 68262 +oid sha256:35c93820d8c23903642e48a41727121fd65b4f8fbf19c624a6183720975a19e5 +size 77994 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Night_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Night_1_null_1,NEXUS_5,1.0,en].png index e45d929e14..aa1122237f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Night_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Night_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bcc61f1337b615ca05d0b6da76a0f434936a9b6bc14ba464ec8c3c8f2d20fe7d -size 80887 +oid sha256:7f37427ccf7fcb57cf17d3d8783b4c90644685e994c651fd0243a95cecdb953c +size 77487 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Night_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Night_1_null_2,NEXUS_5,1.0,en].png index ffef97470f..25fede62c9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Night_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Night_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:62d7f0187da9d4e5d33a63b8f1057bcfed5c63d7175969e37ab1b742d5a2d05a -size 17669 +oid sha256:6778dbbf2060dc0fd63b08f954f62a6353254e6b8c440603229acbdcf74c1048 +size 88159 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Day_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Day_0_null_0,NEXUS_5,1.0,en].png index 0e233e8f64..ff1f8a68f4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Day_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Day_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:244a6f7b697dcbff32ad6010145c2c97f60df2c3b9455560148f4c8265ba36f1 -size 77859 +oid sha256:b56adae1ba8b83b4291aaf54a57a43bc7138b585a26fbf195a4969ba52129181 +size 71820 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Night_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Night_1_null_0,NEXUS_5,1.0,en].png index ff2ddc533c..b05ecbc480 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Night_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Night_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5da6a3d6d4e7657a760054acb68449050ff16c34431fde68f2a718b0c7bd047f -size 75080 +oid sha256:0a5398cd9a06246f0ada91f3e57bbfa13ed5f4b25ed3b48210cb437307a804db +size 68811 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_DialogWithTitleIconAndOkButton_null_Dialogs_Dialogwithtitle,iconandokbutton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_DialogWithTitleIconAndOkButton_null_Dialogs_Dialogwithtitle,iconandokbutton_0_null,NEXUS_5,1.0,en].png index 039267ab46..934848bcdf 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_DialogWithTitleIconAndOkButton_null_Dialogs_Dialogwithtitle,iconandokbutton_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_DialogWithTitleIconAndOkButton_null_Dialogs_Dialogwithtitle,iconandokbutton_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4b8d0f0a54f0124f7eb021fa6f8463390d55863cc1121287687c53ae6fed41ac -size 58156 +oid sha256:eb194f936775a3dccac2d1528e8bc5898d2f91c869f94a7650560a2e140311a7 +size 58157 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_DropdownMenuItem_null_Menus_DropdownMenuItem_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_DropdownMenuItem_null_Menus_DropdownMenuItem_0_null,NEXUS_5,1.0,en].png index ce489e30e4..70802bc779 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_DropdownMenuItem_null_Menus_DropdownMenuItem_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_DropdownMenuItem_null_Menus_DropdownMenuItem_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:127101dd98df815f133e7f1ec1a21673d5552011279007d8964d0694fc529a9d -size 25714 +oid sha256:0e5c5445302ea15f59e8a57d379ada22059ff70824c08920fad909a775b275cb +size 25700 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_FilledButtonLarge_null_Buttons_FilledButtonLarge_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_FilledButtonLarge_null_Buttons_FilledButtonLarge_0_null,NEXUS_5,1.0,en].png index 1ef44d7b1d..8336dab438 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_FilledButtonLarge_null_Buttons_FilledButtonLarge_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_FilledButtonLarge_null_Buttons_FilledButtonLarge_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:222795141c044e5bd7929cb9b894535124a176fd88347eea83037116f4ba6e8b -size 68192 +oid sha256:05005096100ea98d504edf9304be728d1ca0674258ca5a60c5389a695e6875d2 +size 68218 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_FilledButtonMedium_null_Buttons_FilledButtonMedium_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_FilledButtonMedium_null_Buttons_FilledButtonMedium_0_null,NEXUS_5,1.0,en].png index 4d5c49c7a8..3a3da0b295 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_FilledButtonMedium_null_Buttons_FilledButtonMedium_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_FilledButtonMedium_null_Buttons_FilledButtonMedium_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2d816dac6d468ede766719ec854cd575c89f454346ddd6ef8048c836bd04c14 -size 66223 +oid sha256:f97d409d0dad1f9112a2138da074be0034e8471876a721d824f534d9b0bcfade +size 66274 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_FloatingActionButton_null_FloatingActionButtons_FloatingActionButton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_FloatingActionButton_null_FloatingActionButtons_FloatingActionButton_0_null,NEXUS_5,1.0,en].png index f08ca0852f..7b78bc6fe8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_FloatingActionButton_null_FloatingActionButtons_FloatingActionButton_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_FloatingActionButton_null_FloatingActionButtons_FloatingActionButton_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d21a8208c1f5df3ed04281b45066524dccf720c00e9c9386b7c814218cf9c91e -size 11397 +oid sha256:f6f07b94fea9580fd08e74248f6cd2fdd60160c2a28b0cf15ea0ed156740ff7f +size 11509 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_IconButton_null_Buttons_IconButton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_IconButton_null_Buttons_IconButton_0_null,NEXUS_5,1.0,en].png index 1ef5d1f3e2..02782d3abc 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_IconButton_null_Buttons_IconButton_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_IconButton_null_Buttons_IconButton_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de399d6941e299218c65c17d39a63f1568bb4fb543f6001b2136ea57c9d5c65d -size 10038 +oid sha256:db040dea2686c5b223563c34b033e1b95976b5def115d6d4898fcf1ecb4553b0 +size 10467 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_IconImageVector_null_Icons_IconImageVector_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_IconImageVector_null_Icons_IconImageVector_0_null,NEXUS_5,1.0,en].png index 3320c28478..5064a08614 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_IconImageVector_null_Icons_IconImageVector_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_IconImageVector_null_Icons_IconImageVector_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3947efc28ab49d9bbcd2a627bb6ee4daf008293d374820475bc27212dbe41f67 -size 5722 +oid sha256:31d60be0d1387ce99b1dc0d699e09168f5f06fb79a0a5e9249990b884f6bae45 +size 5892 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemDisabledWithIcon_null_Listitems_Listitem-Disabled&Icon_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemDisabledWithIcon_null_Listitems_Listitem-Disabled&Icon_0_null,NEXUS_5,1.0,en].png index 1c50219374..464862fa94 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemDisabledWithIcon_null_Listitems_Listitem-Disabled&Icon_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemDisabledWithIcon_null_Listitems_Listitem-Disabled&Icon_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6cfcc18770fa629f2ca75afb94837966a178734f539393cbc0a02327b0aa3a1e -size 9724 +oid sha256:2a442bdfad9cd00adeda04733a7df2084444f3ede54c7a5d8a3015b73cd9405d +size 9686 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemErrorWithIcon_null_Listitems_Listitem-Error&Icon_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemErrorWithIcon_null_Listitems_Listitem-Error&Icon_0_null,NEXUS_5,1.0,en].png index d2c968cfd6..08a152ae96 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemErrorWithIcon_null_Listitems_Listitem-Error&Icon_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemErrorWithIcon_null_Listitems_Listitem-Error&Icon_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:85e11259b0214eeda3af3833aea3238fd975421cb00f26d989e564bef400e661 -size 9895 +oid sha256:42947273aa5206b61537389a90b75cfb5dc404f99828f592bc1ea2f30f328ec3 +size 9877 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemPrimaryActionWithIcon_null_Listitems_Listitem-Primaryaction&Icon_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemPrimaryActionWithIcon_null_Listitems_Listitem-Primaryaction&Icon_0_null,NEXUS_5,1.0,en].png index 28d6ee0c13..6931e25433 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemPrimaryActionWithIcon_null_Listitems_Listitem-Primaryaction&Icon_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemPrimaryActionWithIcon_null_Listitems_Listitem-Primaryaction&Icon_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:706eea0721a1279648f1344b4a26373adfde1e9ef57655ed089b13d31bed6c0e -size 9870 +oid sha256:7bacfed37b81583aa97949d1a44e98ec81ec825f1ef62d27c26e5182d960961c +size 9848 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemSingleLineBothIcons_null_Listitems_Listitem(1line)-BothIcons_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemSingleLineBothIcons_null_Listitems_Listitem(1line)-BothIcons_0_null,NEXUS_5,1.0,en].png index 4e5751bcf4..93f7aafb6a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemSingleLineBothIcons_null_Listitems_Listitem(1line)-BothIcons_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemSingleLineBothIcons_null_Listitems_Listitem(1line)-BothIcons_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07019c744bdf25bced1ee77b94ca280bf03a612c49b307c40266a1bc37dda73e -size 11775 +oid sha256:72de6bcb547e59838b8ff5b5a990a421303ed3c9d42e792e76ef74e2abfb4f7f +size 11796 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemSingleLineLeadingIcon_null_Listitems_Listitem(1line)-LeadingIcon_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemSingleLineLeadingIcon_null_Listitems_Listitem(1line)-LeadingIcon_0_null,NEXUS_5,1.0,en].png index 6a9d76cbcb..bf3a77c7dc 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemSingleLineLeadingIcon_null_Listitems_Listitem(1line)-LeadingIcon_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemSingleLineLeadingIcon_null_Listitems_Listitem(1line)-LeadingIcon_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68c54765226f37b45f4822dd19bac309c5116299369b1dba8e23332cf8ad82b4 -size 10039 +oid sha256:3caa9918f96e34950a63a24d53ecd1285dc7ea42d4ac6581ea2ad80121a6d1d8 +size 10026 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemSingleLineTrailingIcon_null_Listitems_Listitem(1line)-TrailingIcon_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemSingleLineTrailingIcon_null_Listitems_Listitem(1line)-TrailingIcon_0_null,NEXUS_5,1.0,en].png index a506fefb81..0ae8d95be4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemSingleLineTrailingIcon_null_Listitems_Listitem(1line)-TrailingIcon_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemSingleLineTrailingIcon_null_Listitems_Listitem(1line)-TrailingIcon_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ffb7d0746445d1472083bb2b464305110966409331ab2ff95370ebfda12df397 -size 10035 +oid sha256:354685b1a042ebcee89e5afbf9c45954d6615242d55fb07ad7a0b6638bee1570 +size 10034 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemThreeLinesBothIcons_null_Listitems_Listitem(3lines)-BothIcons_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemThreeLinesBothIcons_null_Listitems_Listitem(3lines)-BothIcons_0_null,NEXUS_5,1.0,en].png index ce00aae7c3..cc80e94ce4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemThreeLinesBothIcons_null_Listitems_Listitem(3lines)-BothIcons_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemThreeLinesBothIcons_null_Listitems_Listitem(3lines)-BothIcons_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e596872da988ae742c022ddbe8634578046b44529d10593e1b054d0b09178803 -size 28289 +oid sha256:1d291b3f0d64c7d6ca9cf39daf65959aafd6438e3a16392459ea2d65e7e9ac4d +size 28279 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemThreeLinesLeadingIcon_null_Listitems_Listitem(3lines)-LeadingIcon_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemThreeLinesLeadingIcon_null_Listitems_Listitem(3lines)-LeadingIcon_0_null,NEXUS_5,1.0,en].png index a71dfaee29..4d22dfbcfb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemThreeLinesLeadingIcon_null_Listitems_Listitem(3lines)-LeadingIcon_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemThreeLinesLeadingIcon_null_Listitems_Listitem(3lines)-LeadingIcon_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1246741e480d23fc8c0137b1434b6768868fe07ad2ac3dbd576e8fbbc78d96b4 -size 26939 +oid sha256:3642b8640e9d8823e887cdb5641afdc6e65078ec43c99c9e3f3068ce3000a8b9 +size 26931 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemThreeLinesTrailingIcon_null_Listitems_Listitem(3lines)-TrailingIcon_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemThreeLinesTrailingIcon_null_Listitems_Listitem(3lines)-TrailingIcon_0_null,NEXUS_5,1.0,en].png index 736e3ce12c..d205083740 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemThreeLinesTrailingIcon_null_Listitems_Listitem(3lines)-TrailingIcon_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemThreeLinesTrailingIcon_null_Listitems_Listitem(3lines)-TrailingIcon_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cf2c7e9650b7d3189931784a1343364e149b2f05fbe1d7b20b7df45d617f9f0c -size 26945 +oid sha256:5b2280812773385a3aeee9d090b2ce6a5bb1e25a426d88ed7c9e33be300262be +size 26942 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemTwoLinesBothIcons_null_Listitems_Listitem(2lines)-BothIcons_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemTwoLinesBothIcons_null_Listitems_Listitem(2lines)-BothIcons_0_null,NEXUS_5,1.0,en].png index 1c19cce902..821fe8cac7 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemTwoLinesBothIcons_null_Listitems_Listitem(2lines)-BothIcons_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemTwoLinesBothIcons_null_Listitems_Listitem(2lines)-BothIcons_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3ee9115e42c92ec140231039b3622e70558ea43d00a1a6f11f4f1aa45da58e1 -size 21613 +oid sha256:d41f40195166fd9fe6d4b20c967d263d8ee3805931d2ec74c6671dd8d02af7b6 +size 21619 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemTwoLinesLeadingIcon_null_Listitems_Listitem(2lines)-LeadingIcon_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemTwoLinesLeadingIcon_null_Listitems_Listitem(2lines)-LeadingIcon_0_null,NEXUS_5,1.0,en].png index 39852f7e8b..bbf2b87535 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemTwoLinesLeadingIcon_null_Listitems_Listitem(2lines)-LeadingIcon_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemTwoLinesLeadingIcon_null_Listitems_Listitem(2lines)-LeadingIcon_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d9c9db6d6372adadb2f5442671e13d0cbd8ea00c7ea7d280acb19164cf578c16 -size 21712 +oid sha256:1064cc1d43235ceb8b0372e17399cb22269523df8bb1353e1068ed6386c2c4e8 +size 21702 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemTwoLinesTrailingIcon_null_Listitems_Listitem(2lines)-TrailingIcon_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemTwoLinesTrailingIcon_null_Listitems_Listitem(2lines)-TrailingIcon_0_null,NEXUS_5,1.0,en].png index 94723fda17..6639da3de0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemTwoLinesTrailingIcon_null_Listitems_Listitem(2lines)-TrailingIcon_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListItemTwoLinesTrailingIcon_null_Listitems_Listitem(2lines)-TrailingIcon_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa6c920555aa0520b8f6719512c76f92038e4853ac8bee3e5686c42718bc7293 -size 21702 +oid sha256:b7f05e42db99e98b190d4b3f85eeb159cdb91cf04f7f0233ac5b6ff34988cb5f +size 21685 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListSupportingTextSmallPadding_null_Listsections_Listsupportingtext-smallpadding_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListSupportingTextSmallPadding_null_Listsections_Listsupportingtext-smallpadding_0_null,NEXUS_5,1.0,en].png index bc54689325..ec1d84bd1c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListSupportingTextSmallPadding_null_Listsections_Listsupportingtext-smallpadding_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ListSupportingTextSmallPadding_null_Listsections_Listsupportingtext-smallpadding_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:90e79fd9f2d16666c1d339776ae8fac04ba879edc946522320d7c672ad9bf805 -size 26577 +oid sha256:ebf58bb06686f62f00595efb8b544e37bd9bcd2e43d5f0da7f710b1875d9d0e5 +size 26567 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_MediumTopAppBar_null_AppBars_MediumTopAppBar_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_MediumTopAppBar_null_AppBars_MediumTopAppBar_0_null,NEXUS_5,1.0,en].png index 7dcedfcb46..0ff402464d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_MediumTopAppBar_null_AppBars_MediumTopAppBar_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_MediumTopAppBar_null_AppBars_MediumTopAppBar_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d52a08dd8698b4367d48cdefb3def8f2256de2b7378d908a12ccad13bdd3fb66 -size 12888 +oid sha256:2aebf73171f1393d1caae765cc4a39fe96cf805a77d26ebe4231962842932030 +size 12859 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_OutlinedButtonLarge_null_Buttons_OutlinedButtonLarge_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_OutlinedButtonLarge_null_Buttons_OutlinedButtonLarge_0_null,NEXUS_5,1.0,en].png index d34a8c1d62..2462b8e6fb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_OutlinedButtonLarge_null_Buttons_OutlinedButtonLarge_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_OutlinedButtonLarge_null_Buttons_OutlinedButtonLarge_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8d423b71168b3bd5dab8da01376fa3f114865aa0d4eb48d41fccfb4640367e3 -size 77867 +oid sha256:36d97466541ddd2d4e2e32b9a57e9782ff6ae22bf20957e6b6cad36cc42a1460 +size 77879 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_OutlinedButtonMedium_null_Buttons_OutlinedButtonMedium_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_OutlinedButtonMedium_null_Buttons_OutlinedButtonMedium_0_null,NEXUS_5,1.0,en].png index be25d4cf37..a0b6c8d710 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_OutlinedButtonMedium_null_Buttons_OutlinedButtonMedium_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_OutlinedButtonMedium_null_Buttons_OutlinedButtonMedium_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5381c6f070f2c81019ce39af5902f6f2c683c7ce4db4636b3b3259fdca270cdf -size 75884 +oid sha256:5d4938c3ec343b4437b41c9e1608e4ddceed3f91ea15e1b840e545a7ea4dbed0 +size 75903 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveEmptyQuery_null_Searchviews_SearchBarActiveEmptyQuery_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveEmptyQuery_null_Searchviews_SearchBarActiveEmptyQuery_0_null,NEXUS_5,1.0,en].png index 584bea65be..9222ea7f83 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveEmptyQuery_null_Searchviews_SearchBarActiveEmptyQuery_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveEmptyQuery_null_Searchviews_SearchBarActiveEmptyQuery_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f66546fc56a478990d29431011668318350c1ebb36eb721017b115d951da27ef -size 8395 +oid sha256:3203990c9d944eaacd53eddbe4f18f3145a4878fc1d29fb267b1edbb97e3e7bb +size 8406 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveWithContent_null_Searchviews_SearchBarActiveWithContent_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveWithContent_null_Searchviews_SearchBarActiveWithContent_0_null,NEXUS_5,1.0,en].png index 73976dc148..8bee26c25a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveWithContent_null_Searchviews_SearchBarActiveWithContent_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveWithContent_null_Searchviews_SearchBarActiveWithContent_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a367bc814cebbdfd7315784cf4c8cb1e9c02d3c35eea8c6597065f67e7cafcf3 -size 25585 +oid sha256:0f1212e4a9e0738e09c70af270d1293bda9b60638ead31ed893fca6c83ba9513 +size 25598 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveWithNoResults_null_Searchviews_SearchBarActiveWithNoResults_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveWithNoResults_null_Searchviews_SearchBarActiveWithNoResults_0_null,NEXUS_5,1.0,en].png index c245c46a4a..b43b180155 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveWithNoResults_null_Searchviews_SearchBarActiveWithNoResults_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveWithNoResults_null_Searchviews_SearchBarActiveWithNoResults_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:45b6ef423c50b68d6e93e9d7674f5e2e79a5fde2ca100d35feb1ee61fd07a57b -size 10132 +oid sha256:c0030a05dda700e04c76b98d26c138b2a1bf1b153e9fcd68297b1246ee2b517f +size 10146 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_null_Searchviews_SearchBarActiveWithQueryNoBackButton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_null_Searchviews_SearchBarActiveWithQueryNoBackButton_0_null,NEXUS_5,1.0,en].png index d2d5289d29..066b8b0bf4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_null_Searchviews_SearchBarActiveWithQueryNoBackButton_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_null_Searchviews_SearchBarActiveWithQueryNoBackButton_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f1c56b67d460b88f8b3ada622149f6d3966755a71d08febabcc8fb4d9203a7a1 -size 7707 +oid sha256:c310b757292155abb999e4902488bd5d1e08345dbe15eab92816f381d1d45cb5 +size 7742 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveWithQuery_null_Searchviews_SearchBarActiveWithQuery_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveWithQuery_null_Searchviews_SearchBarActiveWithQuery_0_null,NEXUS_5,1.0,en].png index fcffb603f5..ae608354f1 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveWithQuery_null_Searchviews_SearchBarActiveWithQuery_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarActiveWithQuery_null_Searchviews_SearchBarActiveWithQuery_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:67bc7d4f6bb49560aa0422d84abb42a59e9f4c2d174370cea54957d1b06f2825 -size 8012 +oid sha256:634e1063be11b91a9a69e21a4d36482dd1e2e298df6812693401cb1a11285192 +size 8030 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarInactive_null_Searchviews_SearchBarInactive_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarInactive_null_Searchviews_SearchBarInactive_0_null,NEXUS_5,1.0,en].png index e43c39827f..4feba68b56 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarInactive_null_Searchviews_SearchBarInactive_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SearchBarInactive_null_Searchviews_SearchBarInactive_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5b5768b8ac2807b1ed4765b0bcfcd0a1e69cde50da9a3aa1fae7ae2791e25c24 -size 15359 +oid sha256:dee7f183fac36d643e4452bd7b78f0e8c45ef5073777d48cea6813924861f7e0 +size 15343 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SnackbarWithActionAndCloseButton_null_Snackbars_Snackbarwithactionandclosebutton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SnackbarWithActionAndCloseButton_null_Snackbars_Snackbarwithactionandclosebutton_0_null,NEXUS_5,1.0,en].png index ed53456e0c..9e0817bd21 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SnackbarWithActionAndCloseButton_null_Snackbars_Snackbarwithactionandclosebutton_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SnackbarWithActionAndCloseButton_null_Snackbars_Snackbarwithactionandclosebutton_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08b390f64db460575ca9461c6bf3cc48955b3d4909fe6013821342f603f2baf9 -size 19422 +oid sha256:e17a8138209a8893a23d7cc40b6b81eec278eadb8048ffe50dd718cf4fdf2bba +size 19465 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_null_Snackbars_Snackbarwithactionandclosebuttononnewline_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_null_Snackbars_Snackbarwithactionandclosebuttononnewline_0_null,NEXUS_5,1.0,en].png index 1527ab6ad3..b738f515dc 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_null_Snackbars_Snackbarwithactionandclosebuttononnewline_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_null_Snackbars_Snackbarwithactionandclosebuttononnewline_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c004a5843ef4ef402ec6437dbabc721fe31d18f9c9fb515ddb7b0daf22c826c9 -size 19831 +oid sha256:f6649339c1700ec8e842e9a82478edf9c73d3998edc0cb7f9a178197073a7771 +size 19879 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TextButtonLarge_null_Buttons_TextButtonLarge_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TextButtonLarge_null_Buttons_TextButtonLarge_0_null,NEXUS_5,1.0,en].png index 2fa4c52ecb..1cd79f4e3a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TextButtonLarge_null_Buttons_TextButtonLarge_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TextButtonLarge_null_Buttons_TextButtonLarge_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:093a5bd68ebca0a9eaf8cac3cf748514cc0f3165c1079016d54d40762c7c0287 -size 46434 +oid sha256:f4a2da532a40e9ab3e9febef795f095c623cb42ec01624e7bbb6391e2e379ec8 +size 46469 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TextButtonMedium_null_Buttons_TextButtonMedium_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TextButtonMedium_null_Buttons_TextButtonMedium_0_null,NEXUS_5,1.0,en].png index 89b4df8c93..61175113c3 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TextButtonMedium_null_Buttons_TextButtonMedium_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TextButtonMedium_null_Buttons_TextButtonMedium_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dfa423f2008049e54046449c19615eb66f28e7e66645eff76e80e33062b5c59a -size 43655 +oid sha256:725a3daadc5786824a9f1e1e864133961e0f29462e18f045b1be29222203e6c0 +size 43682 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TopAppBar_null_AppBars_TopAppBar_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TopAppBar_null_AppBars_TopAppBar_0_null,NEXUS_5,1.0,en].png index 76256beff5..919d1b78d2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TopAppBar_null_AppBars_TopAppBar_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TopAppBar_null_AppBars_TopAppBar_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d285cede9fc617e24b127530e770ed4ff118080714fa1e9071198932898c02fa -size 12292 +oid sha256:2de32ec1881bcfabfde028333bb11ba17c206fa0574a7c62382793be92bfeaad +size 12266 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme_ColorAliases_null_ColorAliases-Day_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme_ColorAliases_null_ColorAliases-Day_0_null,NEXUS_5,1.0,en].png index c23cc8a0e2..9b7597184d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme_ColorAliases_null_ColorAliases-Day_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme_ColorAliases_null_ColorAliases-Day_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dc7900cfc6a214acef16f642a2273088abef88f99cbf68b1aa99e43daab837ca -size 52528 +oid sha256:14e86de237be16c09f2b59582e094881e601531962eb7e9a89ef5ccbcfd260aa +size 62262 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme_ColorAliases_null_ColorAliases-Night_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme_ColorAliases_null_ColorAliases-Night_1_null,NEXUS_5,1.0,en].png index 57ed33c01b..cffae33b80 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme_ColorAliases_null_ColorAliases-Night_1_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme_ColorAliases_null_ColorAliases-Night_1_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:46760e205656e7a071904848c83b5e42d583887ba51d1a5c6682898dc3605143 -size 52611 +oid sha256:19351bb211ab0b6771b2056ca26a11940ee17f849adb095dfb87c5a858c5e646 +size 61353 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AttachmentThumbnail_null_AttachmentThumbnail-Day-0_1_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AttachmentThumbnail_null_AttachmentThumbnail-Day-0_1_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..466ddae77f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AttachmentThumbnail_null_AttachmentThumbnail-Day-0_1_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cb4a67d7d793c0100c6c75ba41cdd2466eaaf391776f78b41a5e7476a44b7a5 +size 4903 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AttachmentThumbnail_null_AttachmentThumbnail-Night-0_2_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AttachmentThumbnail_null_AttachmentThumbnail-Night-0_2_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4d8c6ed456 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AttachmentThumbnail_null_AttachmentThumbnail-Night-0_2_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d2dd62d29f408871c349f6977b6ae58dea3cd2db8109adc7fd28fd83de61bc38 +size 4899 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Day-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Day-1_2_null,NEXUS_5,1.0,en].png index 0bf007326e..8f5625bf68 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Day-1_2_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Day-1_2_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8669ba9062242c83bba1a040f1996289b545121e47866377d3f0199355045f44 -size 15184 +oid sha256:4a95cf1f7fc10b5522069052c271f3aa9c88a2d4ea1de084d89d24ee4498f54b +size 15185 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Night-1_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Night-1_3_null,NEXUS_5,1.0,en].png index 9bd9804b74..cdd009f78e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Night-1_3_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Night-1_3_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:60582e5a930ca64ccae2dda6a3c9dcba5ca82df34c3aede655040feba92043b3 -size 13271 +oid sha256:d491b0b1f0ad879aff5e8bd5c23a2ef7164563d25aa1d4ebeb622eea928a0348 +size 13248 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_CheckableUnresolvedUserRow_null_CheckableUnresolvedUserRow_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_CheckableUnresolvedUserRow_null_CheckableUnresolvedUserRow_0_null,NEXUS_5,1.0,en].png index c23c20ab26..5d459747a8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_CheckableUnresolvedUserRow_null_CheckableUnresolvedUserRow_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_CheckableUnresolvedUserRow_null_CheckableUnresolvedUserRow_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1b097e8b5359de857a2027cefbf26204bd9399bcef97b5bebdf194b2a0e74077 -size 116201 +oid sha256:3dd3995ea7bdf218f55d2883fade50c29558d2ed2faca318c8d18a0eb7f06a34 +size 116197 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Day-6_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Day-6_7_null,NEXUS_5,1.0,en].png index c51ed2f7bc..65e67f3983 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Day-6_7_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Day-6_7_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8659449fd048ce6c3666e5dcfd4412a5d8ab7a302fb73d2282a45bfd2543044f -size 8750 +oid sha256:e8c07a3bfeb657c5a860903e8f820d8b39eb9f25a821b427c1e0ed4dfb84c9af +size 8751 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Night-6_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Night-6_8_null,NEXUS_5,1.0,en].png index 31b251d020..4cef475039 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Night-6_8_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Night-6_8_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a84879fdb00ec04ce11f43ffb20477a46007af593b85ffa4b2dbe1e40f67b8b1 -size 8916 +oid sha256:cd4064903b1c69cefb24f5f1f0feb6da7bcdaf1591b8d843a6fb4997c8a939c9 +size 8919 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUser_null_SelectedUser-Day-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUser_null_SelectedUser-Day-7_8_null,NEXUS_5,1.0,en].png index 193828ce0c..54747853fa 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUser_null_SelectedUser-Day-7_8_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUser_null_SelectedUser-Day-7_8_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5eff8f248a844b07ab8031f51a01a4d94c6b5dd1649f96c565e7d29cd1edabf4 +oid sha256:6d89ee19a1bffa6b149fa0c02fe25c34aaf41feed569ae8417d8033baaee57d9 size 8925 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUser_null_SelectedUser-Night-7_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUser_null_SelectedUser-Night-7_9_null,NEXUS_5,1.0,en].png index 8055c5dc4d..3db41477b1 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUser_null_SelectedUser-Night-7_9_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUser_null_SelectedUser-Night-7_9_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:39705ea5ac180cd9b6864ebf9fbc3d2d6ef1b0892eacd15916738f337ef9caee -size 9057 +oid sha256:1a1514f9734bee019f68ab0b406fcaf50865340675f8a65446e13329e6daa477 +size 9061 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUsersList_null_SelectedUsersList-Day-8_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUsersList_null_SelectedUsersList-Day-8_9_null,NEXUS_5,1.0,en].png index c5ce8b21bf..cc7eb9cfbf 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUsersList_null_SelectedUsersList-Day-8_9_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUsersList_null_SelectedUsersList-Day-8_9_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a14903508a496eab5dd55e43c055d8b915472b21a9d9806fd9aa1a308b0d955 -size 73502 +oid sha256:00af66bdd09859fccf019a39a31a8a7bc0b7dd405deb2255ae27597fab52c02f +size 73539 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUsersList_null_SelectedUsersList-Night-8_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUsersList_null_SelectedUsersList-Night-8_10_null,NEXUS_5,1.0,en].png index 865246e2ff..e4efeac2be 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUsersList_null_SelectedUsersList-Night-8_10_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUsersList_null_SelectedUsersList-Night-8_10_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08e67defd047f92efb8cd86e63ece4ecd5f86cab07383840efffc2e49bf74958 -size 72490 +oid sha256:bdc522c3303ccbdb12ef9585fbfb65197212d1583140d685e860783016f51da1 +size 72521 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_UnresolvedUserRow_null_UnresolvedUserRow_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_UnresolvedUserRow_null_UnresolvedUserRow_0_null,NEXUS_5,1.0,en].png index 7cb0f5eb8e..43341c8967 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_UnresolvedUserRow_null_UnresolvedUserRow_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_UnresolvedUserRow_null_UnresolvedUserRow_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c260ee71b504fd44adcad0e434b424a0dfd574afaa354197f64983177a3d643 -size 32242 +oid sha256:b75ff8f014cd092f271425229b7646997646388232c440865eb73b3e8ae26cf5 +size 32244 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5c2eae4889 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:702e1e894a67b9560aec85de5802a4154d58e69ba2ffbe7707d05880dba3fc95 +size 395463 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e3b185400d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c3048062c7a5ef61db81bb839dd292b012ffdeef0193869f7dab4c766afde1b +size 395465 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_10,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6b7dc973bb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_10,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a475687a9b1684159dba1a08f9737c4f49a19c4b7d49cbf4d0b698403fbc84c +size 394434 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8de38ec545 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:47518d4e8a4a6466fb2bde019b63eb31730f2874396786b23a46b096cc805795 +size 114662 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b7c8af49fb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0acf460a6d70a593946b7f82f4ebec25edbb7598d8b466c344bba4bf44c228f8 +size 395694 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_MediaViewerView_null_MediaViewerView_0_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2fd35cfb41 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cbc856ff05a44a7d9a3b7ccf9335bd7ae925fc050a567cd61a0b2628c4226088 +size 6652 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..1661c69849 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a0549cc366b71a4ed78005ae685e19e22fa0561af30ff458ae7d96f1e8144bc +size 15921 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8aad25a27a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:242786cdc406fb647e36573ad1a29396a2e67b1cf5563e7a9173e9cf51ecfbf1 +size 16093 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..664b225425 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e6280c209c1f89cad0aa1c2cd60c1dc9ed47259989f445c19a7c0341473c093 +size 14430 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_9,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..3ac034c99a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.mediaviewer.api.viewer_MediaViewerView_null_MediaViewerView_0_null_9,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c350f8cb07bd107b3d3041ee31299b4c3294d68e7b57ce236fa38d10df8b84b7 +size 14539 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Day-0_1_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..89014c7ad4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Day-0_1_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:05fae38cdda7b3e00a355ecade04c54b5fa2921a529aa51af7cf90122caa9769 +size 14972 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Day-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Day-0_1_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..499c9f356d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Day-0_1_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:83af8ee61dc0ddf6fdbbe58d49266d78d3a0809c57b91ea5995bd1c47b1c00f9 +size 12648 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Day-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Day-0_1_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f9020eea6e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Day-0_1_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3edf5f5df7879c16ccdd4536d94f28e98d1226ab8fed423887a23b4aa31f44e9 +size 26647 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Day-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Day-0_1_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6133cfb5a8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Day-0_1_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15b55914bbaffae07629f3b3a8c4f6e798b604ed49a2c55f9c9cd825c882ba27 +size 24815 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Day-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Day-0_1_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..644fb3261d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Day-0_1_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61298b0953efb79896508b5ccda84ce3a1784916fb6846e9070dac066a66d3d0 +size 29384 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Night-0_2_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c0c063e434 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Night-0_2_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:764689e0dc5b509a5c36493ab7454ff5dc0c269fab6c68c57cbd89198b254506 +size 13853 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Night-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Night-0_2_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7aa04c331b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Night-0_2_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:323b97bd6852a87e23ebb29f06a8406931de786d0bc702cd464718ec8ba7f8e4 +size 11799 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Night-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Night-0_2_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7c3da240b3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Night-0_2_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a446d7ff4f2fbf54a7a686ed04cb34a3085b69166292e355e3afac7e8dbd045 +size 25413 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Night-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Night-0_2_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e4fb22ce6b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Night-0_2_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51d5c50784721a89047c2ce9ed29585d876b7f2abcda399638a372c65e83b9c2 +size 23763 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Night-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Night-0_2_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f1f83d8845 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.roomselect.impl_RoomSelectView_null_RoomSelectView-Night-0_2_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b1263552c90b03257daea6fa1a5ccef6c37f8b25e88133db279a1c8b120692e8 +size 27467 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_ComposerOptionsButton_null_ComposerOptionsButton-Day-8_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_ComposerOptionsButton_null_ComposerOptionsButton-Day-8_9_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_ComposerOptionsButton_null_ComposerOptionsButton-Day-8_8_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_ComposerOptionsButton_null_ComposerOptionsButton-Day-8_9_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_ComposerOptionsButton_null_ComposerOptionsButton-Night-8_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_ComposerOptionsButton_null_ComposerOptionsButton-Night-8_10_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_ComposerOptionsButton_null_ComposerOptionsButton-Night-8_9_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_ComposerOptionsButton_null_ComposerOptionsButton-Night-8_10_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_DismissTextFormattingButton_null_DismissTextFormattingButton-Day-9_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_DismissTextFormattingButton_null_DismissTextFormattingButton-Day-9_10_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_DismissTextFormattingButton_null_DismissTextFormattingButton-Day-9_9_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_DismissTextFormattingButton_null_DismissTextFormattingButton-Day-9_10_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_DismissTextFormattingButton_null_DismissTextFormattingButton-Night-9_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_DismissTextFormattingButton_null_DismissTextFormattingButton-Night-9_11_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_DismissTextFormattingButton_null_DismissTextFormattingButton-Night-9_10_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_DismissTextFormattingButton_null_DismissTextFormattingButton-Night-9_11_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_FormattingButton_null_FormattingButton-Day-10_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_FormattingButton_null_FormattingButton-Day-10_11_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_FormattingButton_null_FormattingButton-Day-10_10_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_FormattingButton_null_FormattingButton-Day-10_11_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_FormattingButton_null_FormattingButton-Night-10_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_FormattingButton_null_FormattingButton-Night-10_12_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_FormattingButton_null_FormattingButton-Night-10_11_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_FormattingButton_null_FormattingButton-Night-10_12_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_LiveWaveformView_null_LiveWaveformView-Day-11_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_LiveWaveformView_null_LiveWaveformView-Day-11_12_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_LiveWaveformView_null_LiveWaveformView-Day-11_11_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_LiveWaveformView_null_LiveWaveformView-Day-11_12_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_LiveWaveformView_null_LiveWaveformView-Night-11_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_LiveWaveformView_null_LiveWaveformView-Night-11_13_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_LiveWaveformView_null_LiveWaveformView-Night-11_12_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_LiveWaveformView_null_LiveWaveformView-Night-11_13_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Day-12_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Day-12_12_null,NEXUS_5,1.0,en].png deleted file mode 100644 index d32c73e543..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Day-12_12_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:60410832f66d0c70166443d855743ef956b1b97a3684624ce5797805d71e04f6 -size 8712 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Day-12_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Day-12_13_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8322cd733a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Day-12_13_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5688650b7609c65b495dc2d4cb5023940788eed9c425a8af5f1cd5203a794258 +size 9058 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Night-12_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Night-12_13_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 93591c72a9..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Night-12_13_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:54c5208fd88e1f404667cdc0b39ff87824115a1c414dfdbd55a334fa6a445548 -size 8619 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Night-12_14_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Night-12_14_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6b4ccba457 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Night-12_14_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:45b73f239fb480eba8122ca29ada6d64d66ea3218d0602eb3b6171aa7f81fbe8 +size 8984 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Day-13_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Day-13_14_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Day-13_13_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Day-13_14_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Night-13_14_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Night-13_15_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Night-13_14_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Night-13_15_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Day-14_14_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Day-14_14_null,NEXUS_5,1.0,en].png deleted file mode 100644 index b09c425cf1..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Day-14_14_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1ad3be335771242252bbb2ce0e6944e1a53cb9e4bca81fd4566ccd79cf85ae95 -size 5213 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Day-14_15_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Day-14_15_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..391dba96a7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Day-14_15_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ab72a7ebb21f5eb893e18d02c19d07a61b55566c23a4c6ba4c851d1ed1f36fc +size 5211 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Night-14_15_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Night-14_15_null,NEXUS_5,1.0,en].png deleted file mode 100644 index cd311e4906..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Night-14_15_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:60f913b80137673f83c973b4e05de2b3303c478d85cbd8a8c9edc20e8fabdd45 -size 5168 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Night-14_16_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Night-14_16_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..fcdf7f6f95 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Night-14_16_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e7330a35e0a64b5ec3374949bb6ec2c5463c3a98d8b9ef19a526b971a31b86f +size 5166 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Day-15_15_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Day-15_16_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Day-15_15_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Day-15_16_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Night-15_16_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Night-15_17_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Night-15_16_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Night-15_17_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Day-16_16_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Day-16_16_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 3059b1e5ad..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Day-16_16_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6b6f6caef8d902d4e0b93721f212dcd3f20cde267e1782813f0c55498bca5c8e -size 6805 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Day-16_17_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Day-16_17_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..35f7667c89 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Day-16_17_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eff4df85d88cbb65856e565ed6f02f3dc4cde59b0972d895799baac16a57b894 +size 6816 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Night-16_17_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Night-16_17_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 279ced000a..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Night-16_17_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6def3059e37f5c7d6d8930df229a895f5d37b95f46a138fa4ef790bbf76b80b6 -size 6744 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Night-16_18_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Night-16_18_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7167bd1c50 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Night-16_18_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:938a6181d5f7714224f8a0ef5704f49dbeb0bccb7cabc50ffde46605aa0563fd +size 6744 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Day-17_17_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Day-17_18_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Day-17_17_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Day-17_18_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Night-17_18_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Night-17_19_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Night-17_18_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Night-17_19_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Day-18_18_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Day-18_18_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 87bfdadf60..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Day-18_18_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a70e858c4935324773d885cdc80268550af9ded8bfa2c7e5e6b916f2d4b85f4b -size 18110 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Day-18_19_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Day-18_19_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c152a6cc4b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Day-18_19_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e541cfb32e9d3095d6499c4c25e4bb4158c10886d59accf6006290bda344e8b2 +size 43927 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Night-18_19_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Night-18_19_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 2b93b20917..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Night-18_19_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3c0a05c8f8245af9d46da170ce3062f20c10b77f7b64be1432747a14b98c5c0e -size 16198 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Night-18_20_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Night-18_20_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..17f88d03b3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Night-18_20_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:06cd8d611bf8149d3c86b8d9a4aa5613dc9f32f89156081472f98d6410e98c4d +size 37107 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerEdit_null_TextComposerEdit-Day-2_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerEdit_null_TextComposerEdit-Day-2_2_null,NEXUS_5,1.0,en].png deleted file mode 100644 index e6274d3ab9..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerEdit_null_TextComposerEdit-Day-2_2_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a0c5c53f53eb3cdda73391fabda2658b5c009e9a13761ef9acae37530f3cb1b5 -size 14017 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerEdit_null_TextComposerEdit-Day-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerEdit_null_TextComposerEdit-Day-2_3_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ff30c39f49 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerEdit_null_TextComposerEdit-Day-2_3_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c8aa2ae4af7a4d0a24b6ebe47331e4f81d2d97c95f31601d494df29cb7ab2d36 +size 14020 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerEdit_null_TextComposerEdit-Night-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerEdit_null_TextComposerEdit-Night-2_3_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 59f70b2c3c..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerEdit_null_TextComposerEdit-Night-2_3_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e5b786b07d92459399099e0f7730804de2f2123d8c5eb51ca64f7609768ecc34 -size 13157 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerEdit_null_TextComposerEdit-Night-2_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerEdit_null_TextComposerEdit-Night-2_4_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..06b5e4b672 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerEdit_null_TextComposerEdit-Night-2_4_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ece212524733f0e20f2eb3517467926ff0597d8023175f79e2e07e37279e23b6 +size 13156 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerFormatting_null_TextComposerFormatting-Day-1_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerFormatting_null_TextComposerFormatting-Day-1_1_null,NEXUS_5,1.0,en].png deleted file mode 100644 index bfc1d62dbc..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerFormatting_null_TextComposerFormatting-Day-1_1_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:54f6712d9cbd60c6a6d108ddcff08492a5e74cbba7d1f4b5f3312a068e4dd798 -size 43207 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerFormatting_null_TextComposerFormatting-Day-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerFormatting_null_TextComposerFormatting-Day-1_2_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7c0c8c2eba --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerFormatting_null_TextComposerFormatting-Day-1_2_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4c61883a8351b6da7ca8f84e1ffb68c2edfea874dce83b969c76f6793def91c +size 43725 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerFormatting_null_TextComposerFormatting-Night-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerFormatting_null_TextComposerFormatting-Night-1_2_null,NEXUS_5,1.0,en].png deleted file mode 100644 index e30b7ac05a..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerFormatting_null_TextComposerFormatting-Night-1_2_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e156ed6831abb52d8bbd95934517f423d0cae06e21e9266b099174aad55228ab -size 40662 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerFormatting_null_TextComposerFormatting-Night-1_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerFormatting_null_TextComposerFormatting-Night-1_3_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..baddaffe44 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerFormatting_null_TextComposerFormatting-Night-1_3_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e676d39404aa5ee5af019733a8ecd7a605bc6984f3d8fde15d9bdcf9b24823df +size 41316 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_null_TextComposerLinkDialogCreateLinkWithoutText-Day-6_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_null_TextComposerLinkDialogCreateLinkWithoutText-Day-6_7_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_null_TextComposerLinkDialogCreateLinkWithoutText-Day-6_6_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_null_TextComposerLinkDialogCreateLinkWithoutText-Day-6_7_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_null_TextComposerLinkDialogCreateLinkWithoutText-Night-6_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_null_TextComposerLinkDialogCreateLinkWithoutText-Night-6_8_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_null_TextComposerLinkDialogCreateLinkWithoutText-Night-6_7_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_null_TextComposerLinkDialogCreateLinkWithoutText-Night-6_8_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogCreateLink_null_TextComposerLinkDialogCreateLink-Day-5_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogCreateLink_null_TextComposerLinkDialogCreateLink-Day-5_6_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogCreateLink_null_TextComposerLinkDialogCreateLink-Day-5_5_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogCreateLink_null_TextComposerLinkDialogCreateLink-Day-5_6_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogCreateLink_null_TextComposerLinkDialogCreateLink-Night-5_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogCreateLink_null_TextComposerLinkDialogCreateLink-Night-5_7_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogCreateLink_null_TextComposerLinkDialogCreateLink-Night-5_6_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogCreateLink_null_TextComposerLinkDialogCreateLink-Night-5_7_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogEditLink_null_TextComposerLinkDialogEditLink-Day-7_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogEditLink_null_TextComposerLinkDialogEditLink-Day-7_8_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogEditLink_null_TextComposerLinkDialogEditLink-Day-7_7_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogEditLink_null_TextComposerLinkDialogEditLink-Day-7_8_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogEditLink_null_TextComposerLinkDialogEditLink-Night-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogEditLink_null_TextComposerLinkDialogEditLink-Night-7_9_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogEditLink_null_TextComposerLinkDialogEditLink-Night-7_8_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerLinkDialogEditLink_null_TextComposerLinkDialogEditLink-Night-7_9_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerReply_null_TextComposerReply-Day-3_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerReply_null_TextComposerReply-Day-3_3_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 9c64fdc104..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerReply_null_TextComposerReply-Day-3_3_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:339df847ee60d9b2b6da7458b9b99c54877551325cfcdcefe8677f4f9edd72e3 -size 87578 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerReply_null_TextComposerReply-Day-3_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerReply_null_TextComposerReply-Day-3_4_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e29869a669 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerReply_null_TextComposerReply-Day-3_4_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e1be39d46e0ee8bf6986b3d177fa3dc7bc44307b1bc8c2ddad64441da561211a +size 88167 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerReply_null_TextComposerReply-Night-3_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerReply_null_TextComposerReply-Night-3_4_null,NEXUS_5,1.0,en].png deleted file mode 100644 index b0073ea6e0..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerReply_null_TextComposerReply-Night-3_4_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ff51194566d9c09bb054cf7be73147215ec267382ce7bf0552f4b8de6bb357ec -size 83398 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerReply_null_TextComposerReply-Night-3_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerReply_null_TextComposerReply-Night-3_5_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..04b53af7e3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerReply_null_TextComposerReply-Night-3_5_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2e4a71b5a437fc369c11c48665b00c59cf8720cde94f57eb8358895c5318bf6b +size 84067 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerSimple_null_TextComposerSimple-Day-0_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerSimple_null_TextComposerSimple-Day-0_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index f00037d6dd..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerSimple_null_TextComposerSimple-Day-0_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f50efca595312474668341a2bb6715992286bb52d4645774ed9fb1a83f94ca9f -size 48112 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerSimple_null_TextComposerSimple-Day-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerSimple_null_TextComposerSimple-Day-0_1_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..90341e2651 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerSimple_null_TextComposerSimple-Day-0_1_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74c96bb584a8b837ae7142b1a394515c2211ec471015fc497a70ff2ae8517b19 +size 48687 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerSimple_null_TextComposerSimple-Night-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerSimple_null_TextComposerSimple-Night-0_1_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 9d14e51ab1..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerSimple_null_TextComposerSimple-Night-0_1_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7c50736a0b06dd848b79b263cdb231fa9b3ae23ad049615d6073ce1838b8ca5f -size 45219 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerSimple_null_TextComposerSimple-Night-0_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerSimple_null_TextComposerSimple-Night-0_2_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a28880c04e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerSimple_null_TextComposerSimple-Night-0_2_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5dc7210d64bf4297020655c482a0459ab7dc280481f2add42ddd1b11835fde50 +size 45948 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Day-4_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Day-4_4_null,NEXUS_5,1.0,en].png deleted file mode 100644 index e9254642a2..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Day-4_4_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9a76e1dcbc4461b6e0a1cca836a097846262a3d302befbec4486ed7ee837a8da -size 28214 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Day-4_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Day-4_5_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..54ba757f3e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Day-4_5_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6ae5e1ef943fb5f0e0badf41d4ce6317117ab15c8ce8d1bb3a1712260f7f637b +size 28703 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Night-4_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Night-4_5_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 304d6dc23e..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Night-4_5_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:25be7fa172232c508c58369f60e82fc7ec1d7362d9f701c1e698ec8d8498b482 -size 27449 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Night-4_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Night-4_6_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..68a5a8fe7e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Night-4_6_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a498224c5657212ae19d2854c687a6057735f12521c2a97db9f5754fb23ecfd +size 27828 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.theme_ColorsSchemeDark_null_ColorsSchemeDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.theme_ColorsSchemeDark_null_ColorsSchemeDark_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index bf374ef0e6..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.theme_ColorsSchemeDark_null_ColorsSchemeDark_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fc8de41db4a71b5462de8ea069a4ec9de9e31b7d6d1440e7dd39b03bdefb9ed5 -size 116891 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.theme_ColorsSchemeLight_null_ColorsSchemeLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.theme_ColorsSchemeLight_null_ColorsSchemeLight_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 81e48b4c56..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.theme_ColorsSchemeLight_null_ColorsSchemeLight_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:436dcbc518179a5bc2a36785b7a8465e2a5ef4076121df01d1b2ac1e4f47fa06 -size 113712 diff --git a/tools/check/forbidden_strings_in_code.txt b/tools/check/forbidden_strings_in_code.txt index 17f352fca4..37234ac670 100755 --- a/tools/check/forbidden_strings_in_code.txt +++ b/tools/check/forbidden_strings_in_code.txt @@ -132,3 +132,7 @@ System\.currentTimeMillis\(\)===1 ### Use `import io.element.android.libraries.ui.strings.CommonStrings` then `CommonStrings.` instead import io\.element\.android\.libraries\.ui\.strings\.R + +# Accessibility +### Use string resource for `contentDescription`, or null instead of empty string +contentDescription = " diff --git a/tools/danger/dangerfile.js b/tools/danger/dangerfile.js index d183911c39..b9f46d5666 100644 --- a/tools/danger/dangerfile.js +++ b/tools/danger/dangerfile.js @@ -38,7 +38,7 @@ if (requiresChangelog) { const changelogFiles = editedFiles.filter(file => file.startsWith("changelog.d/")) if (changelogFiles.length == 0) { - warn("Please add a changelog. See instructions [here](https://github.com/vector-im/element-android/blob/develop/CONTRIBUTING.md#changelog)") + warn("Please add a changelog. See instructions [here](https://github.com/element-hq/element-android/blob/develop/CONTRIBUTING.md#changelog)") } else { const validTowncrierExtensions = [ "bugfix", @@ -48,7 +48,7 @@ if (requiresChangelog) { "wip", ] if (!changelogFiles.every(file => validTowncrierExtensions.includes(file.split(".").pop()))) { - fail("Invalid extension for changelog. See instructions [here](https://github.com/vector-im/element-android/blob/develop/CONTRIBUTING.md#changelog)") + fail("Invalid extension for changelog. See instructions [here](https://github.com/element-hq/element-android/blob/develop/CONTRIBUTING.md#changelog)") } } } @@ -107,11 +107,11 @@ if (allowList.includes(user)) { signoff_unneeded("allow-list") } else { // github.api.rest.orgs.checkMembershipForUser({ -// org: "vector-im", +// org: "element-hq", // username: user, // }).then((result) => { github.api.rest.teams.getMembershipForUserInOrg({ - org: "vector-im", + org: "element-hq", team_slug: "vector-core", username: user, }).then((result) => { @@ -172,7 +172,7 @@ const translationAllowList = [ if (!translationAllowList.includes(user)) { if (editedFiles.some(file => file.endsWith("strings.xml") && !file.endsWith("values/strings.xml"))) { - fail("Some translation files have been edited. Only user `RiotTranslateBot` (i.e. translations coming from Weblate) or `github-actions[bot]` (i.e. translations coming from automation) are allowed to do that.\nPlease read more about translations management [in the doc](https://github.com/vector-im/element-android/blob/develop/CONTRIBUTING.md#internationalisation).") + fail("Some translation files have been edited. Only user `RiotTranslateBot` (i.e. translations coming from Weblate) or `github-actions[bot]` (i.e. translations coming from automation) are allowed to do that.\nPlease read more about translations management [in the doc](https://github.com/element-hq/element-android/blob/develop/CONTRIBUTING.md#internationalisation).") } // Check that new strings are not added to `values/strings.xml` diff --git a/tools/github/download_github_artifacts.py b/tools/github/download_github_artifacts.py index 892a4affa6..4424c0e009 100755 --- a/tools/github/download_github_artifacts.py +++ b/tools/github/download_github_artifacts.py @@ -65,7 +65,7 @@ if args.verbose: print(args) # Split the artifact URL to get information -# Ex: https://github.com/vector-im/element-android/suites/9293388174/artifacts/435942121 +# Ex: https://github.com/element-hq/element-android/suites/9293388174/artifacts/435942121 artifactUrl = args.artifactUrl if not artifactUrl.startswith('https://github.com/'): print("❌ Invalid parameter --artifactUrl %s. Must start with 'https://github.com/'" % artifactUrl) diff --git a/tools/localazy/config.json b/tools/localazy/config.json index e611db953c..977fe6c889 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -1,91 +1,91 @@ { - "modules": [ + "modules" : [ { - "name": ":features:rageshake:impl", - "includeRegex": [ + "name" : ":features:rageshake:impl", + "includeRegex" : [ "screen_bug_report_.*" ] }, { - "name": ":features:rageshake:api", - "includeRegex": [ + "name" : ":features:rageshake:api", + "includeRegex" : [ "crash_detection_.*", "rageshake_detection_.*", "settings_rageshake.*" ] }, { - "name": ":features:logout:impl", - "includeRegex": [ + "name" : ":features:logout:impl", + "includeRegex" : [ "screen_signout_.*" ] }, { - "name": ":features:onboarding:impl", - "includeRegex": [ + "name" : ":features:onboarding:impl", + "includeRegex" : [ "screen_onboarding_.*" ] }, { - "name": ":features:signedout:impl", - "includeRegex": [ + "name" : ":features:signedout:impl", + "includeRegex" : [ "screen_signed_out_.*" ] }, { - "name": ":features:invitelist:impl", - "includeRegex": [ + "name" : ":features:invitelist:impl", + "includeRegex" : [ "screen_invites_.*" ] }, { - "name": ":features:createroom:impl", - "includeRegex": [ + "name" : ":features:createroom:impl", + "includeRegex" : [ "screen_create_room_.*", "screen_start_chat_.*" ] }, { - "name": ":features:verifysession:impl", - "includeRegex": [ + "name" : ":features:verifysession:impl", + "includeRegex" : [ "screen_session_verification_.*" ] }, { - "name": ":libraries:textcomposer:impl", - "includeRegex": [ + "name" : ":libraries:textcomposer:impl", + "includeRegex" : [ "rich_text_editor.*", ".*voice_message_tooltip" ] }, { - "name": ":libraries:permissions:api", - "includeRegex": [ + "name" : ":libraries:permissions:api", + "includeRegex" : [ "dialog\\.permission_.*" ] }, { - "name": ":libraries:androidutils", - "includeRegex": [ + "name" : ":libraries:androidutils", + "includeRegex" : [ "error_no_compatible_app_found" ] }, { - "name": ":libraries:eventformatter:impl", - "includeRegex": [ + "name" : ":libraries:eventformatter:impl", + "includeRegex" : [ "state_event_.*" ] }, { - "name": ":libraries:push:impl", - "includeRegex": [ + "name" : ":libraries:push:impl", + "includeRegex" : [ "push_.*", "notification_.*" ] }, { - "name": ":features:login:impl", - "includeRegex": [ + "name" : ":features:login:impl", + "includeRegex" : [ "screen_login_.*", "screen_server_confirmation_.*", "screen_change_server_.*", @@ -95,32 +95,35 @@ ] }, { - "name": ":features:leaveroom:api", - "includeRegex": [ + "name" : ":features:leaveroom:api", + "includeRegex" : [ "leave_room_alert_.*" ] }, { - "name": ":features:roomlist:impl", - "includeRegex": [ + "name" : ":features:roomlist:impl", + "includeRegex" : [ "screen_roomlist_.*", "session_verification_banner_.*", "confirm_recovery_key_banner_.*" ] }, { - "name": ":features:roomdetails:impl", - "includeRegex": [ + "name" : ":features:roomdetails:impl", + "includeRegex" : [ "screen_room_details_.*", "screen_room_member_list_.*", "screen_dm_details_.*", "screen_room_notification_settings_.*", - "screen_notification_settings_edit_failed_updating_default_mode" + "screen_notification_settings_edit_failed_updating_default_mode", + "screen_polls_history_title", + "screen_notification_settings_mentions_only_disclaimer", + "screen_start_chat_error_starting_chat" ] }, { - "name": ":features:messages:impl", - "includeRegex": [ + "name" : ":features:messages:impl", + "includeRegex" : [ "room_timeline_.*", "screen_room_.*", "screen\\.room\\..*", @@ -128,49 +131,51 @@ "emoji_picker_category_.*", ".*report_content_.*" ], - "excludeRegex": [ + "excludeRegex" : [ "screen_room_details_.*", "screen_room_member.*", "screen_dm_.*" ] }, { - "name": ":features:analytics:impl", - "includeRegex": [ + "name" : ":features:analytics:impl", + "includeRegex" : [ "screen_analytics_prompt.*" ] }, { - "name": ":features:analytics:api", - "includeRegex": [ + "name" : ":features:analytics:api", + "includeRegex" : [ "screen_analytics_settings_.*" ] }, { - "name": ":features:ftue:impl", - "includeRegex": [ + "name" : ":features:ftue:impl", + "includeRegex" : [ "screen_welcome_.*", "screen_migration_.*", "screen_notification_optin_.*" ] }, { - "name": ":features:poll:impl", - "includeRegex": [ - "screen_create_poll_.*" + "name" : ":features:poll:impl", + "includeRegex" : [ + "screen_create_poll_.*", + "screen_edit_poll_.*", + "screen_polls_history_.*" ] }, { - "name": ":features:securebackup:impl", - "includeRegex": [ + "name" : ":features:securebackup:impl", + "includeRegex" : [ "screen_chat_backup_.*", "screen_key_backup_disable_.*", "screen_recovery_key_.*" ] }, { - "name": ":features:preferences:impl", - "includeRegex": [ + "name" : ":features:preferences:impl", + "includeRegex" : [ "screen_advanced_settings_.*", "screen\\.advanced_settings\\..*", "screen_edit_profile_.*", @@ -178,14 +183,14 @@ ] }, { - "name": ":features:call", - "includeRegex": [ + "name" : ":features:call", + "includeRegex" : [ "call_.*" ] }, { - "name": ":features:lockscreen:impl", - "includeRegex": [ + "name" : ":features:lockscreen:impl", + "includeRegex" : [ "screen_app_lock_.*", "screen_signout_in_progress_dialog_content" ] diff --git a/tools/localazy/downloadStrings.sh b/tools/localazy/downloadStrings.sh index bc00488258..c4a97868af 100755 --- a/tools/localazy/downloadStrings.sh +++ b/tools/localazy/downloadStrings.sh @@ -46,6 +46,14 @@ if [[ $allFiles == 1 ]]; then find . -name 'translations.xml' -print0 -exec bash -c "echo \"\" >> \"{}\"" \; >> /dev/null fi +set +e +echo "Moving files from values-id to values-in..." +find . -type d -name 'values-id' -execdir mv {}/translations.xml {}/../values-in/translations.xml 2> /dev/null \; + +echo "Deleting all the folders values-id..." +find . -type d -name 'values-id' -exec rm -rf {} 2> /dev/null \; +set -e + echo "Removing the generated config" rm ./tools/localazy/localazy.json diff --git a/tools/localazy/generateLocalazyConfig.py b/tools/localazy/generateLocalazyConfig.py index 464c67c70a..ffdbe1f2c8 100755 --- a/tools/localazy/generateLocalazyConfig.py +++ b/tools/localazy/generateLocalazyConfig.py @@ -31,7 +31,7 @@ baseAction = { } } -# Store all regex specific to module, to eclude the corresponding keyx from the common string module +# Store all regex specific to module, to exclude the corresponding key from the common string module allRegexToExcludeFromMainModule = [] # All actions that will be serialized in the localazy config allActions = [] @@ -47,7 +47,7 @@ for entry in config["modules"]: "includeKeys": list(map(lambda i: "REGEX:" + i, entry["includeRegex"])), "excludeKeys": list(map(lambda i: "REGEX:" + i, excludeRegex)), "conditions": [ - "equals: ${languageCode}, en" + "equals: ${languageCode}, en | equals: ${file}, content.json" ] } # print(action) @@ -59,7 +59,7 @@ for entry in config["modules"]: "includeKeys": list(map(lambda i: "REGEX:" + i, entry["includeRegex"])), "excludeKeys": list(map(lambda i: "REGEX:" + i, excludeRegex)), "conditions": [ - "!equals: ${languageCode}, en" + "!equals: ${languageCode}, en | equals: ${file}, content.json" ] } allActions.append(actionTranslation) @@ -70,7 +70,7 @@ mainAction = baseAction | { "output": "libraries/ui-strings/src/main/res/values/localazy.xml", "excludeKeys": list(map(lambda i: "REGEX:" + i, allRegexToExcludeFromMainModule + regexToAlwaysExclude)), "conditions": [ - "equals: ${languageCode}, en" + "equals: ${languageCode}, en | equals: ${file}, content.json" ] } # print(mainAction) @@ -82,7 +82,7 @@ if allFiles: "output": "libraries/ui-strings/src/main/res/values-${langAndroidResNoScript}/translations.xml", "excludeKeys": list(map(lambda i: "REGEX:" + i, allRegexToExcludeFromMainModule + regexToAlwaysExclude)), "conditions": [ - "!equals: ${languageCode}, en" + "!equals: ${languageCode}, en | equals: ${file}, content.json" ] } allActions.append(mainActionTranslation) diff --git a/tools/release/release.sh b/tools/release/release.sh index a5701ea5e1..26a94cb09e 100755 --- a/tools/release/release.sh +++ b/tools/release/release.sh @@ -166,7 +166,7 @@ printf -v versionMinor2Digits "%02d" ${versionMinor} printf -v versionPatch2Digits "%02d" ${versionPatch} fastlaneFile="4${versionMajor2Digits}${versionMinor2Digits}${versionPatch2Digits}0.txt" fastlanePathFile="./fastlane/metadata/android/en-US/changelogs/${fastlaneFile}" -printf "Main changes in this version: TODO.\nFull changelog: https://github.com/vector-im/element-x-android/releases" > ${fastlanePathFile} +printf "Main changes in this version: TODO.\nFull changelog: https://github.com/element-hq/element-x-android/releases" > ${fastlanePathFile} read -p "I have created the file ${fastlanePathFile}, please edit it and press enter to continue. " git add ${fastlanePathFile} @@ -217,7 +217,7 @@ else fi printf "\n================================================================================\n" -printf "Wait for the GitHub action https://github.com/vector-im/element-x-android/actions/workflows/release.yml?query=branch%%3Amain to build the 'main' branch.\n" +printf "Wait for the GitHub action https://github.com/element-hq/element-x-android/actions/workflows/release.yml?query=branch%%3Amain to build the 'main' branch.\n" read -p "After GHA is finished, please enter the artifact URL (for 'elementx-app-bundle-unsigned'): " artifactUrl printf "\n================================================================================\n" @@ -292,7 +292,7 @@ else fi printf "\n================================================================================\n" -githubCreateReleaseLink="https://github.com/vector-im/element-x-android/releases/new?tag=v${version}&title=Element%20X%20Android%20v${version}&body=${changelogUrlEncoded}" +githubCreateReleaseLink="https://github.com/element-hq/element-x-android/releases/new?tag=v${version}&title=Element%20X%20Android%20v${version}&body=${changelogUrlEncoded}" printf "Creating the release on gitHub.\n" printf -- "Open this link: %s\n" ${githubCreateReleaseLink} printf "Then\n" @@ -303,7 +303,7 @@ read -p ". Press enter to continue. " printf "\n================================================================================\n" printf "Message for the Android internal room:\n\n" -message="@room Element X Android ${version} is ready to be tested. You can get it from https://github.com/vector-im/element-x-android/releases/tag/v${version}. Installation instructions can be found [here](https://github.com/vector-im/element-x-android/blob/develop/docs/install_from_github_release.md). Please report any feedback. Thanks!" +message="@room Element X Android ${version} is ready to be tested. You can get it from https://github.com/element-hq/element-x-android/releases/tag/v${version}. Installation instructions can be found [here](https://github.com/element-hq/element-x-android/blob/develop/docs/install_from_github_release.md). Please report any feedback. Thanks!" printf "${message}\n\n" if [[ -z "${elementBotToken}" ]]; then diff --git a/tools/sas/import_sas_emojis.py b/tools/sas/import_sas_emojis.py new file mode 100755 index 0000000000..0c7f539af1 --- /dev/null +++ b/tools/sas/import_sas_emojis.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 + +# 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. + +import argparse +import json +import os +import os.path +# Run `pip3 install requests` if not installed yet +import requests + +### Arguments + +parser = argparse.ArgumentParser(description='Download sas string from matrix-doc.') +parser.add_argument('-v', + '--verbose', + help="increase output verbosity.", + action="store_true") + +args = parser.parse_args() + +if args.verbose: + print("Argument:") + print(args) + +base_url = "https://raw.githubusercontent.com/matrix-org/matrix-spec/main/data-definitions/sas-emoji.json" + +base_emoji_url = "https://raw.githubusercontent.com/twitter/twemoji/master/assets/svg/" + +print("Downloading " + base_url + "…") + +r0 = requests.get(base_url) +data0 = json.loads(r0.content.decode()) + +if args.verbose: + print("Json data:") + print(data0) + +print() + +scripts_dir = os.path.dirname(os.path.abspath(__file__)) +data_defs_dir = os.path.join(scripts_dir, "../../tmp/emoji/") + +def handle_emoji(dict): + print("Handle emoji " + str(dict["number"]) + " (" + dict["description"] + ")…") + if args.verbose: + print("With") + print(dict) + # Transform dict["unicode"] from "U+2601U+FE0F" to "2601U" + emoji = dict["unicode"].split("U+")[1].lower() + url = base_emoji_url + emoji + ".svg" + file = os.path.join(data_defs_dir, "ic_verification_" + format(dict["number"], '02d') + ".svg") + print("Downloading " + url + " to " + file + "…") + r = requests.get(url) + if r.status_code != 200: + print("Fatal: " + str(r.status_code)) + # Stop script with error + sys.exit(1) + os.makedirs(os.path.dirname(file), exist_ok=True) + with open(file, "w") as f: + f.write(r.content.decode()) + +for emoji in data0: + handle_emoji(emoji) + +print() +print("Success!") +print() +print("To convert to vector drawable, download tool from https://www.androiddesignpatterns.com/2018/11/android-studio-svg-to-vector-cli.html") +print("unzip it, and run:") +print("vd-tool/bin/vd-tool -c -in ./tmp/emoji -out features/verifysession/impl/src/main/res/drawable") diff --git a/tools/sas/import_sas_strings.py b/tools/sas/import_sas_strings.py new file mode 100755 index 0000000000..5f6d948133 --- /dev/null +++ b/tools/sas/import_sas_strings.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 + +# 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. + +import argparse +import json +import os +import os.path +# Run `pip3 install requests` if not installed yet +import requests + +### Arguments + +parser = argparse.ArgumentParser(description='Download sas string from matrix-doc.') +parser.add_argument('-v', + '--verbose', + help="increase output verbosity.", + action="store_true") + +args = parser.parse_args() + +if args.verbose: + print("Argument:") + print(args) + +base_url = "https://raw.githubusercontent.com/matrix-org/matrix-spec/main/data-definitions/sas-emoji.json" + +print("Downloading " + base_url + "…") + +r0 = requests.get(base_url) +data0 = json.loads(r0.content.decode()) + +if args.verbose: + print("Json data:") + print(data0) + +print() + +# number -> translation +default = dict() +# Language -> emoji -> translation +cumul = dict() + +for emoji in data0: + d = dict() + number = emoji["number"] + d["description"] = emoji["description"] + d["emoji"] = emoji["emoji"] + d["unicode"] = emoji["unicode"] + if args.verbose: + print("Dict: " + str(d)) + default[number] = d + + for lang in emoji["translated_descriptions"]: + if args.verbose: + print("Lang: " + lang) + if not (lang in cumul): + cumul[lang] = dict() + d = dict() + d["description"] = emoji["translated_descriptions"][lang] + cumul[lang][number] = d + +if args.verbose: + print(default) + print(cumul) + +def write_file(file, dict): + print("Writing file " + file) + if args.verbose: + print("With") + print(dict) + os.makedirs(os.path.dirname(file), exist_ok=True) + with open(file, mode="w", encoding="utf8") as o: + o.write("\n") + o.write("\n") + o.write(" \n") + for key in dict: + if dict[key] is None: + continue + if dict[key]["description"] is None: + continue + o.write(" " + dict[key]["description"].replace("'", "\\'") + "\n") + o.write("\n") + +scripts_dir = os.path.dirname(os.path.abspath(__file__)) +data_defs_dir = os.path.join(scripts_dir, "../../features/verifysession/impl/src/main/res") + +# Write default file +write_file(os.path.join(data_defs_dir, "values/strings_sas.xml"), default) + +# Write each language file +for lang in cumul: + androidLang = lang \ + .replace("_", "-r") \ + .replace("zh-rHans", "zh-rCN") \ + .replace("zh-rHant", "zh-rTW") \ + .replace("id", "in") + write_file(os.path.join(data_defs_dir, "values-" + androidLang + "/strings_sas.xml"), cumul[lang]) + +print() +print("Success!") diff --git a/tools/sdk/build_rust_sdk.sh b/tools/sdk/build_rust_sdk.sh index 3d7dc17059..93c0150f87 100755 --- a/tools/sdk/build_rust_sdk.sh +++ b/tools/sdk/build_rust_sdk.sh @@ -8,6 +8,7 @@ read -p "Do you want to build the Rust SDK from local source (yes/no) default to buildLocal=${buildLocal:-yes} date=$(gdate +%Y%m%d%H%M%S) +elementPwd=`pwd` # Ask for the Rust SDK local source path # if folder rustSdk/ exists, use it as default @@ -28,7 +29,7 @@ else cd matrix-rust-sdk-$date git checkout ${rustSdkBranch} rustSdkPath=$(pwd) - cd ../element-x-android + cd ${elementPwd} fi @@ -46,6 +47,8 @@ fi read -p "Do you want to build the app after (yes/no) default to yes? " buildApp buildApp=${buildApp:-yes} +cd ${elementPwd} + # If folder ../matrix-rust-components-kotlin does not exist, clone the repo if [ ! -d "../matrix-rust-components-kotlin" ]; then printf "\nFolder ../matrix-rust-components-kotlin does not exist. Cloning the repository into ../matrix-rust-components-kotlin.\n\n" @@ -59,9 +62,9 @@ git checkout main git pull printf "\nBuilding the SDK for aarch64-linux-android...\n\n" -./scripts/build.sh -p ${rustSdkPath} -m sdk -t aarch64-linux-android -o ../element-x-android/libraries/rustsdk +./scripts/build.sh -p ${rustSdkPath} -m sdk -t aarch64-linux-android -o ${elementPwd}/libraries/rustsdk -cd ../element-x-android +cd ${elementPwd} mv ./libraries/rustsdk/sdk-android-debug.aar ./libraries/rustsdk/matrix-rust-sdk.aar mkdir -p ./libraries/rustsdk/sdks cp ./libraries/rustsdk/matrix-rust-sdk.aar ./libraries/rustsdk/sdks/matrix-rust-sdk-${date}.aar diff --git a/towncrier.toml b/towncrier.toml index c9be3af199..e018467aec 100644 --- a/towncrier.toml +++ b/towncrier.toml @@ -3,7 +3,7 @@ filename = "CHANGES.md" name = "Changes in Element X" template = "tools/towncrier/template.md" - issue_format = "[#{issue}](https://github.com/vector-im/element-x-android/issues/{issue})" + issue_format = "[#{issue}](https://github.com/element-hq/element-x-android/issues/{issue})" [[tool.towncrier.type]] directory = "feature"