mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-02 12:07:28 +00:00
Merge branch 'release/26.09.2'
This commit is contained in:
@@ -10,6 +10,14 @@
|
||||
"gradle",
|
||||
],
|
||||
"minimumReleaseAge": "7 days",
|
||||
"hostRules": [
|
||||
{
|
||||
"matchHost": "https://repo1.maven.org/maven2/",
|
||||
"concurrentRequestLimit": 10,
|
||||
"maxRequestsPerSecond": 10,
|
||||
"timeout": 10000,
|
||||
}
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"matchPackageNames": [
|
||||
@@ -17,6 +25,20 @@
|
||||
"/^io.element.android/",
|
||||
],
|
||||
"minimumReleaseAge": null,
|
||||
// Max default value is 6, with this we give absolute priority to our own dependencies
|
||||
"prPriority": 7,
|
||||
"registryUrls": [ "https://repo1.maven.org/maven2/" ],
|
||||
},
|
||||
{
|
||||
// Fetch Android dependencies from Google's Maven repo instead of Maven Central.
|
||||
"matchPackageNames": [
|
||||
"/^androidx\\./",
|
||||
"/^android\\./",
|
||||
"/^com\\.android\\.tools/",
|
||||
"/^com\\.google\\.firebase/",
|
||||
"/^com\\.google\\.android/",
|
||||
],
|
||||
"registryUrls": [ "https://dl.google.com/android/maven2/" ],
|
||||
},
|
||||
{
|
||||
"groupName": "kotlin",
|
||||
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||
persist-credentials: false
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
- name: Clone submodules
|
||||
run: git submodule update --init --recursive
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
name: Danger CI
|
||||
|
||||
on: [pull_request, merge_group]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
name: Danger main check
|
||||
# Skip in forks, it doesn't work even with the fallback token
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
- name: Clone submodules
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
run: git submodule update --init --recursive
|
||||
- run: |
|
||||
npm install --save-dev @babel/plugin-transform-flow-strip-types
|
||||
- name: Danger
|
||||
uses: danger/danger-js@e9b58820039524318198d8059ec98b0c02b7875c # 13.0.10
|
||||
with:
|
||||
args: "--dangerfile ./tools/danger/dangerfile.js"
|
||||
env:
|
||||
DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
|
||||
# Fallback for forks
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
- name: ⏬ Checkout with LFS
|
||||
uses: nschloe/action-cached-lfs-checkout@385a8ecc719e50b8c71af6ab01a624b486b7c3bc # v1.2.5
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
- uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
name: Use JDK 21
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
|
||||
@@ -20,6 +20,9 @@ jobs:
|
||||
build-apk:
|
||||
name: Build APK
|
||||
runs-on: ubuntu-latest
|
||||
# Secrets are not available to pull_request runs from forks, so the Maestro job below
|
||||
# cannot run for them; skip the build too, as its only consumer is that job.
|
||||
if: github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
concurrency:
|
||||
group: ${{ format('maestro-build-{0}', github.ref) }}
|
||||
cancel-in-progress: true
|
||||
@@ -44,7 +47,7 @@ jobs:
|
||||
# https://github.com/actions/checkout/issues/881
|
||||
ref: ${{ github.ref }}
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
- uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
name: Use JDK 21
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
@@ -73,13 +76,16 @@ jobs:
|
||||
name: Maestro test suite
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ build-apk ]
|
||||
if: github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
# Allow only one to run at a time, since they use the same environment.
|
||||
# Otherwise, tests running in parallel can break each other.
|
||||
# queue: max lets several runs wait their turn instead of the default, where a
|
||||
# newly queued run cancels the one already pending (that run then never gets a verdict).
|
||||
concurrency:
|
||||
group: maestro-test
|
||||
queue: max
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
if: (github.event_name == 'pull_request' && github.event.pull_request.fork == null) || github.event_name == 'workflow_dispatch'
|
||||
with:
|
||||
# Ensure we are building the branch and not the branch after being merged on develop
|
||||
# https://github.com/actions/checkout/issues/881
|
||||
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
uses: nschloe/action-cached-lfs-checkout@385a8ecc719e50b8c71af6ab01a624b486b7c3bc # v1.2.5
|
||||
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
@@ -80,7 +80,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
name: PR Checks
|
||||
|
||||
on:
|
||||
# Privilege escalation necessary to comment on pull requests from forks.
|
||||
# 🚨 We must not check out or execute any code here, and be careful around use of user-controlled inputs.
|
||||
pull_request_target: # zizmor: ignore[dangerous-triggers]
|
||||
types: [ opened, reopened, edited, synchronize, labeled, unlabeled ]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
pr-checks:
|
||||
name: PR Checks
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
|
||||
concurrency:
|
||||
# Only allow a single run of this workflow on each branch, automatically cancelling older runs.
|
||||
group: ${{ format('pr-checks-{0}', github.ref) }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
# Required to comment on the pull request.
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Check the pull request
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
// Classes that must not be modified anymore. Add the file name suffix here to freeze a class.
|
||||
const FROZEN_CLASSES = [
|
||||
];
|
||||
|
||||
// Only this user is allowed to edit the translation files, since they come from Localazy.
|
||||
const TRANSLATION_ALLOW_LIST = ["ElementBot"];
|
||||
|
||||
const pr = context.payload.pull_request;
|
||||
const body = pr.body ?? "";
|
||||
|
||||
const files = await github.paginate(github.rest.pulls.listFiles, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: pr.number
|
||||
});
|
||||
// Deleted files are not interesting for any of the checks below.
|
||||
const paths = files.filter(file => file.status !== "removed").map(file => file.filename);
|
||||
|
||||
const warnings = [];
|
||||
const failures = [];
|
||||
|
||||
if (!body) {
|
||||
warnings.push("Please provide a description for this PR.");
|
||||
}
|
||||
|
||||
if (paths.length > 50) {
|
||||
warnings.push("This pull request seems relatively large. Please consider splitting it into multiple smaller ones.");
|
||||
}
|
||||
|
||||
if (pr.title.endsWith("…")) {
|
||||
failures.push("Please provide a complete title that can be used as a changelog entry.");
|
||||
}
|
||||
|
||||
if (pr.labels.filter(label => label.name.startsWith("PR-")).length !== 1) {
|
||||
failures.push("Please add a single `PR-` label to categorise the changelog entry.");
|
||||
}
|
||||
|
||||
for (const frozen of FROZEN_CLASSES) {
|
||||
if (paths.some(path => path.endsWith(frozen))) {
|
||||
failures.push(`Frozen class \`${frozen}\` has been modified. Please do not modify frozen class.`);
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude the recorded screenshots, which are legitimately pngs.
|
||||
if (paths.some(path => path.toLowerCase().endsWith(".png") && !path.includes("snapshots/images/"))) {
|
||||
warnings.push("You seem to have made changes to some images. Please consider using a vector drawable.");
|
||||
}
|
||||
|
||||
if (!TRANSLATION_ALLOW_LIST.includes(pr.user.login) && paths.some(path => path.endsWith("translations.xml"))) {
|
||||
failures.push("Some translation files have been edited. Only user `ElementBot` (i.e. translations coming from Localazy) is allowed to do that. " +
|
||||
"Please read more about translations management [in the doc](https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md#strings).");
|
||||
}
|
||||
|
||||
warnings.forEach(warning => core.warning(warning));
|
||||
failures.forEach(failure => core.error(failure));
|
||||
|
||||
// Keep a single comment up to date rather than adding a new one on every run.
|
||||
const marker = "<!-- pr-checks -->";
|
||||
const comments = await github.paginate(github.rest.issues.listComments, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number
|
||||
});
|
||||
const existing = comments.find(comment => comment.body.includes(marker));
|
||||
|
||||
if (warnings.length || failures.length) {
|
||||
const lines = [...failures.map(failure => `- ❌ ${failure}`),
|
||||
...warnings.map(warning => `- ⚠️ ${warning}`)];
|
||||
const commentBody = `${marker}\n### PR Checks\n\n${lines.join("\n")}`;
|
||||
|
||||
if (existing) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: existing.id,
|
||||
body: commentBody
|
||||
});
|
||||
} else {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
body: commentBody
|
||||
});
|
||||
}
|
||||
} else if (existing) {
|
||||
await github.rest.issues.deleteComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: existing.id
|
||||
});
|
||||
}
|
||||
|
||||
if (failures.length) {
|
||||
core.setFailed(`${failures.length} check(s) failed, see the comment on the pull request.`);
|
||||
}
|
||||
+166
-28
@@ -69,7 +69,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
@@ -108,7 +108,7 @@ jobs:
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
run: git submodule update --init --recursive
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
@@ -149,7 +149,7 @@ jobs:
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
run: git submodule update --init --recursive
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
@@ -183,7 +183,7 @@ jobs:
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
run: git submodule update --init --recursive
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
@@ -228,7 +228,7 @@ jobs:
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
run: git submodule update --init --recursive
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
@@ -269,7 +269,7 @@ jobs:
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
run: git submodule update --init --recursive
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
@@ -336,34 +336,172 @@ jobs:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: zizmorcore/zizmor-action@3dc1ecc9bcb9e94e9b2c709687979e1298497054 # v0.6.2
|
||||
- uses: zizmorcore/zizmor-action@70fb788f84895a7701f5643d103d587e460b5c99 # v0.6.3
|
||||
|
||||
upload_reports:
|
||||
name: Project Check Suite
|
||||
runs-on: ubuntu-latest
|
||||
needs: [konsist, lint, ktlint, detekt]
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
# Run even if a previous job failed: that is precisely when there are issues to report.
|
||||
if: ${{ !cancelled() }}
|
||||
permissions:
|
||||
# Required to list the files modified by the pull request.
|
||||
pull-requests: read
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
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 }}
|
||||
persist-credentials: false
|
||||
- name: Download reports from previous jobs
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
- name: Prepare Danger
|
||||
if: always()
|
||||
run: |
|
||||
npm install --save-dev @babel/core
|
||||
npm install --save-dev @babel/plugin-transform-flow-strip-types
|
||||
yarn add danger-plugin-lint-report --dev
|
||||
- name: Danger lint
|
||||
if: always()
|
||||
uses: danger/danger-js@e9b58820039524318198d8059ec98b0c02b7875c # 13.0.10
|
||||
- name: Annotate the lint, ktlint and detekt issues
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
args: "--dangerfile ./tools/danger/dangerfile-lint.js"
|
||||
env:
|
||||
DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
|
||||
# Fallback for forks
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
// GitHub only surfaces a handful of annotations per level and per step, so cap what we emit
|
||||
// and let the job summary carry the full list.
|
||||
const MAX_ANNOTATIONS = 20;
|
||||
const MAX_SUMMARY_ROWS = 200;
|
||||
|
||||
function unescapeXml(value) {
|
||||
return value
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, "\"")
|
||||
.replace(/'/g, "'")
|
||||
.replace(/&#(\d+);/g, (_, code) => String.fromCharCode(Number(code)))
|
||||
.replace(/&/g, "&");
|
||||
}
|
||||
|
||||
function attribute(tag, name) {
|
||||
const match = new RegExp(`\\b${name}="([^"]*)"`).exec(tag);
|
||||
return match ? unescapeXml(match[1]) : undefined;
|
||||
}
|
||||
|
||||
// Paths in the reports are absolute paths on the runner which generated them, i.e.
|
||||
// /home/runner/work/<repo>/<repo>/<path>.
|
||||
const workspace = `/work/${context.repo.repo}/${context.repo.repo}/`;
|
||||
function repoRelative(path) {
|
||||
const index = path.indexOf(workspace);
|
||||
return index === -1 ? path : path.slice(index + workspace.length);
|
||||
}
|
||||
|
||||
// Checkstyle XML, as produced by ktlint and detekt:
|
||||
// <file name="…"><error line=".." column=".." severity=".." message=".." source=".."/></file>
|
||||
function parseCheckstyle(content) {
|
||||
const issues = [];
|
||||
const fileRegex = /<file\s+([^>]*?)(\/>|>([\s\S]*?)<\/file>)/g;
|
||||
let file;
|
||||
while ((file = fileRegex.exec(content)) !== null) {
|
||||
const path = attribute(file[1], "name");
|
||||
if (!path) continue;
|
||||
const errorRegex = /<error\s+([^>]*?)\/?>/g;
|
||||
let error;
|
||||
while ((error = errorRegex.exec(file[3] ?? "")) !== null) {
|
||||
issues.push({
|
||||
file: repoRelative(path),
|
||||
line: Number(attribute(error[1], "line")) || 1,
|
||||
column: Number(attribute(error[1], "column")) || undefined,
|
||||
severity: (attribute(error[1], "severity") ?? "warning").toLowerCase(),
|
||||
rule: attribute(error[1], "source"),
|
||||
message: attribute(error[1], "message") ?? "",
|
||||
});
|
||||
}
|
||||
}
|
||||
return issues;
|
||||
}
|
||||
|
||||
// Android lint XML, which is not Checkstyle:
|
||||
// <issue id=".." severity=".." message=".."><location file=".." line=".." column=".."/></issue>
|
||||
function parseAndroidLint(content) {
|
||||
const issues = [];
|
||||
const issueRegex = /<issue\s+([^>]*?)(\/>|>([\s\S]*?)<\/issue>)/g;
|
||||
let issue;
|
||||
while ((issue = issueRegex.exec(content)) !== null) {
|
||||
const location = /<location\s+([^>]*?)\/?>/.exec(issue[3] ?? "");
|
||||
if (!location) continue;
|
||||
const path = attribute(location[1], "file");
|
||||
if (!path) continue;
|
||||
issues.push({
|
||||
file: repoRelative(path),
|
||||
line: Number(attribute(location[1], "line")) || 1,
|
||||
column: Number(attribute(location[1], "column")) || undefined,
|
||||
severity: (attribute(issue[1], "severity") ?? "warning").toLowerCase(),
|
||||
rule: attribute(issue[1], "id"),
|
||||
message: attribute(issue[1], "message") ?? "",
|
||||
});
|
||||
}
|
||||
return issues;
|
||||
}
|
||||
|
||||
const reports = await (await glob.create("**/reports/**/*.xml")).glob();
|
||||
core.info(`Found ${reports.length} XML report(s).`);
|
||||
|
||||
const fs = require("fs");
|
||||
let issues = [];
|
||||
for (const report of reports) {
|
||||
const content = fs.readFileSync(report, "utf8");
|
||||
if (content.includes("<checkstyle")) {
|
||||
issues.push(...parseCheckstyle(content));
|
||||
} else if (content.includes("<issues")) {
|
||||
issues.push(...parseAndroidLint(content));
|
||||
}
|
||||
}
|
||||
|
||||
// Only report on the files touched by the pull request.
|
||||
const pr = context.payload.pull_request;
|
||||
if (pr) {
|
||||
const files = await github.paginate(github.rest.pulls.listFiles, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: pr.number
|
||||
});
|
||||
const changed = new Set(files.map(file => file.filename));
|
||||
issues = issues.filter(issue => changed.has(issue.file));
|
||||
}
|
||||
|
||||
const seen = new Set();
|
||||
issues = issues.filter(issue => {
|
||||
const key = `${issue.file}:${issue.line}:${issue.rule}:${issue.message}`;
|
||||
if (seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (issues.length === 0) {
|
||||
core.info("No issue found in the files modified by this pull request.");
|
||||
return;
|
||||
}
|
||||
|
||||
const level = severity => {
|
||||
if (severity === "error" || severity === "fatal") return "error";
|
||||
if (severity === "warning") return "warning";
|
||||
return "notice";
|
||||
};
|
||||
const rank = { error: 0, warning: 1, notice: 2 };
|
||||
issues.sort((a, b) =>
|
||||
rank[level(a.severity)] - rank[level(b.severity)] ||
|
||||
a.file.localeCompare(b.file) ||
|
||||
a.line - b.line
|
||||
);
|
||||
|
||||
issues.slice(0, MAX_ANNOTATIONS).forEach(issue => {
|
||||
const annotation = {
|
||||
title: issue.rule,
|
||||
file: issue.file,
|
||||
startLine: issue.line,
|
||||
startColumn: issue.column,
|
||||
};
|
||||
core[level(issue.severity)](issue.message, annotation);
|
||||
});
|
||||
|
||||
core.summary.addHeading(`${issues.length} code quality issue(s)`, 3);
|
||||
core.summary.addTable([
|
||||
[{ data: "Severity", header: true }, { data: "File", header: true }, { data: "Rule", header: true }, { data: "Message", header: true }],
|
||||
...issues.slice(0, MAX_SUMMARY_ROWS).map(issue => [
|
||||
level(issue.severity),
|
||||
`${issue.file}:${issue.line}`,
|
||||
issue.rule ?? "",
|
||||
issue.message,
|
||||
]),
|
||||
]);
|
||||
if (issues.length > MAX_SUMMARY_ROWS) {
|
||||
core.summary.addRaw(`…and ${issues.length - MAX_SUMMARY_ROWS} more, see the uploaded reports.`, true);
|
||||
}
|
||||
await core.summary.write();
|
||||
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: ☕️ Use JDK 21
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
@@ -88,7 +88,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||
persist-credentials: false
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
|
||||
@@ -7,6 +7,9 @@ on:
|
||||
|
||||
permissions: {}
|
||||
|
||||
env:
|
||||
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
|
||||
|
||||
jobs:
|
||||
sync-localazy:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -17,7 +20,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
run: git submodule update --init --recursive
|
||||
- name: ☕️ Use JDK 21
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
|
||||
Generated
-1
@@ -16,7 +16,6 @@
|
||||
<w>posthog</w>
|
||||
<w>rageshake</w>
|
||||
<w>securebackup</w>
|
||||
<w>showkase</w>
|
||||
<w>snackbar</w>
|
||||
<w>spdx</w>
|
||||
<w>swipeable</w>
|
||||
|
||||
+41
@@ -1,3 +1,44 @@
|
||||
Changes in Element X v26.09.1
|
||||
=============================
|
||||
|
||||
## What's Changed
|
||||
### ✨ Features
|
||||
* Support creating polls with multiple answers and send response with multiple answers by @bmarty in https://github.com/element-hq/element-x-android/pull/7613
|
||||
### 🙌 Improvements
|
||||
* Only track read receipts on message-like events by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7361
|
||||
* Stop rescanning message bodies for emoji only on every recomposition by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7531
|
||||
### 🐛 Bugfixes
|
||||
* Ensure that noisy notifications are still noisy when the app has a PIN code. by @bmarty in https://github.com/element-hq/element-x-android/pull/7584
|
||||
* Ensure that sessionVerifiedStatus is updated when the crypto is ready. by @bmarty in https://github.com/element-hq/element-x-android/pull/7604
|
||||
* Fix invalid placeholders in translations by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7612
|
||||
### 🧱 Build
|
||||
* Do not open the editor when finishing the release by @bmarty in https://github.com/element-hq/element-x-android/pull/7583
|
||||
* [release] Check that there is no unmerged PR with the label "Z-NextRelease", else exit by @bmarty in https://github.com/element-hq/element-x-android/pull/7606
|
||||
### Dependency upgrades
|
||||
* Update dependency net.zetetic:sqlcipher-android to v4.18.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7552
|
||||
* Update dependency com.google.firebase:firebase-bom to v34.18.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7581
|
||||
* Update dependency io.github.sergio-sastre.ComposablePreviewScanner:android to v0.9.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7535
|
||||
* Update dependency com.google.guava:guava to v33.7.1-android by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7534
|
||||
* Update dependency org.unifiedpush.android:connector to v3.3.4 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7586
|
||||
* Update dependency org.unifiedpush.android:connector to v3.3.5 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7595
|
||||
* Update roborazzi to v1.73.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7618
|
||||
### 💼 Element Pro
|
||||
* Implement a hook allowing enterprise modules to perform actions on startup by @richvdh in https://github.com/element-hq/element-x-android/pull/7585
|
||||
### Others
|
||||
* Don't hide the thread of a deleted message inside the removed messages group by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7571
|
||||
* Name a sender the timeline has no profile for from the room member list by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7539
|
||||
* Offer conversations in the system share sheet by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7574
|
||||
* Rank the mention suggestions by how well they match what was typed by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7536
|
||||
* Log the SDK git Sha at application startup by @bmarty in https://github.com/element-hq/element-x-android/pull/7589
|
||||
* Limit usage of nullable callback. by @bmarty in https://github.com/element-hq/element-x-android/pull/7588
|
||||
* Fix crash due to JSON.stringify on a cirular structure in injectes js. by @toger5 in https://github.com/element-hq/element-x-android/pull/7600
|
||||
* Support multi-select polls by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6477
|
||||
* Remove ExternalSession, fallback login with Element Web is gone. by @bmarty in https://github.com/element-hq/element-x-android/pull/7608
|
||||
* Fall back to the message body when the formatted body has nothing to render by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7523
|
||||
|
||||
|
||||
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.08.4...v26.09.1
|
||||
|
||||
Changes in Element X v26.08.4
|
||||
=============================
|
||||
|
||||
|
||||
@@ -288,6 +288,10 @@ dependencies {
|
||||
implementation(projects.libraries.pushproviders.unifiedpush)
|
||||
}
|
||||
|
||||
// Google Play Services fused location backend. Discovered at runtime via ServiceLoader by the
|
||||
// location feature.
|
||||
"gplayImplementation"(libs.maplibre.compose.location.runtime.gms)
|
||||
|
||||
implementation(libs.appyx.core)
|
||||
implementation(libs.androidx.splash)
|
||||
implementation(libs.androidx.core)
|
||||
@@ -325,6 +329,7 @@ licensee {
|
||||
allow("BSD-3-Clause")
|
||||
allow("EPL-1.0")
|
||||
allowUrl("https://opensource.org/license/bsd-3-clause")
|
||||
allowUrl("https://opensource.org/license/bsd-2-clause")
|
||||
allowUrl("https://opensource.org/licenses/MIT")
|
||||
allowUrl("https://developer.android.com/studio/terms.html")
|
||||
allowUrl("https://www.zetetic.net/sqlcipher/license/")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<!-- File generated by importSupportedLocalesFromLocalazy.py, do not edit -->
|
||||
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<locale android:name="ar"/>
|
||||
<locale android:name="be"/>
|
||||
<locale android:name="bg"/>
|
||||
<locale android:name="ca"/>
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="banner_migrate_to_native_sliding_sync_action">"Abmelden und aktualisieren"</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_app_force_logout_title">"%1$s unterstützt das alte Protokoll nicht mehr. Bitte melde dich ab und wieder an, um die App weiter nutzen zu können."</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Dein Homeserver unterstützt das alte Protokoll nicht mehr. Bitte logge dich aus und melde dich wieder an, um die App weiter zu nutzen."</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Dein Kontoanbieter unterstützt das alte Protokoll nicht mehr. Bitte melde dich ab und wieder an, um die App weiter zu nutzen."</string>
|
||||
</resources>
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="banner_migrate_to_native_sliding_sync_action">"Αποσύνδεση & Αναβάθμιση"</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_app_force_logout_title">"Το %1$s δεν υποστηρίζει πλέον το παλιό πρωτόκολλο. Αποσυνδεθείτε και συνδεθείτε ξανά για να συνεχίσετε να χρησιμοποιείτε την εφαρμογή."</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Ο οικιακός διακομιστής σου δεν υποστηρίζει πλέον το παλιό πρωτόκολλο. Αποσυνδέσου και συνδέσου ξανά για να συνεχίσεις να χρησιμοποιείς την εφαρμογή."</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Ο πάροχος του λογαριασμού σας δεν υποστηρίζει πλέον το παλιό πρωτόκολλο. Αποσυνδεθείτε και συνδεθείτε ξανά για να συνεχίσετε να χρησιμοποιείτε την εφαρμογή."</string>
|
||||
</resources>
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="banner_migrate_to_native_sliding_sync_action">"Déconnecter et mettre à niveau"</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_app_force_logout_title">"%1$s ne prend plus en charge l’ancien protocole. Veuillez vous déconnecter puis vous reconnecter pour continuer à utiliser l’application."</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Votre serveur d’accueil ne prend plus en charge l’ancien protocole. Veuillez vous déconnecter puis vous reconnecter pour continuer à utiliser l’application."</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Votre fournisseur de compte ne prend plus en charge l’ancien protocole. Veuillez vous déconnecter puis vous reconnecter pour continuer à utiliser l’application."</string>
|
||||
</resources>
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="banner_migrate_to_native_sliding_sync_action">"Esci e aggiorna"</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_app_force_logout_title">"%1$s non supporta più il vecchio protocollo. Esci e accedi nuovamente per continuare a utilizzare l\'app."</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Il tuo homeserver non supporta più il vecchio protocollo. Esci e rientra per continuare a usare l\'app."</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Il tuo gestore dell\'account non supporta più il vecchio protocollo. Esci ed accedi di nuovo per continuare ad utilizzare l\'app."</string>
|
||||
</resources>
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="banner_migrate_to_native_sliding_sync_action">"Wyloguj się i zaktualizuj"</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_app_force_logout_title">"%1$s już nie wspiera starego protokołu. Zaloguj się ponownie, aby dalej korzystać z aplikacji."</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Twój serwer domowy już nie wspiera starego protokołu. Zaloguj się ponownie, aby kontynuować korzystanie z aplikacji."</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Twój dostawca konta już nie wspiera starego protokołu. Zaloguj się ponownie, aby kontynuować korzystanie z aplikacji."</string>
|
||||
</resources>
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="banner_migrate_to_native_sliding_sync_action">"Выйти и обновить"</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_app_force_logout_title">"%1$s больше не поддерживает старый протокол. Пожалуйста, выйдите из системы и войдите снова, чтобы продолжить использование приложения."</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Ваш домашний сервер больше не поддерживает старый протокол. Пожалуйста, выйдите и войдите в свою учётную запись снова, чтобы продолжить использование приложения."</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Ваш сервер больше не поддерживает старый протокол. Пожалуйста, выйдите и войдите в свою учётную запись снова, чтобы продолжить использование приложения."</string>
|
||||
</resources>
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="banner_migrate_to_native_sliding_sync_action">"Вийти та оновити"</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_app_force_logout_title">"%1$s більше не підтримує старий протокол. Вийдіть і знов увійдіть, щоб продовжити користуватися застосунком."</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Ваш домашній сервер більше не підтримує старий протокол. Будь ласка, вийдіть і увійдіть знову, щоб продовжити використання програми."</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Постачальник вашого облікового запису більше не підтримує старий протокол. Вийдіть і знову ввійдіть, щоб продовжити користуватися застосунком."</string>
|
||||
</resources>
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="banner_migrate_to_native_sliding_sync_action">"注销并升级"</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_app_force_logout_title">"%1$s 不再支持旧协议。请注销并重新登录以继续使用该应用程序。"</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"你的主服务器不再支持旧协议。请注销并重新登录以继续使用此 app。"</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"你的账户提供者不再支持旧协议。请注销并重新登录以继续使用此 app。"</string>
|
||||
</resources>
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="banner_migrate_to_native_sliding_sync_action">"Log Out & Upgrade"</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_app_force_logout_title">"%1$s no longer supports the old protocol. Please log out and log back in to continue using the app."</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Your account provider no longer supports the old protocol. Please sign out and sign back in to continue using the app."</string>
|
||||
</resources>
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ allprojects {
|
||||
verbose = true
|
||||
reporters {
|
||||
reporter(org.jlleitschuh.gradle.ktlint.reporter.ReporterType.PLAIN)
|
||||
// To have XML report for Danger
|
||||
// To have XML report for the CI to annotate the PR, see .github/workflows/quality.yml
|
||||
reporter(org.jlleitschuh.gradle.ktlint.reporter.ReporterType.CHECKSTYLE)
|
||||
}
|
||||
val generatedPath = "${layout.buildDirectory.asFile.get()}/generated/"
|
||||
|
||||
@@ -358,7 +358,8 @@ Some dependency, mainly because they are not shared can be declared in `build.gr
|
||||
We have 3 tests frameworks in place, and this should be sufficient to guarantee a good code coverage and limit regressions hopefully:
|
||||
|
||||
- Maestro to test the global usage of the application. See the related [documentation](../.maestro/README.md).
|
||||
- Combination of [Showkase](https://github.com/airbnb/Showkase) and [Paparazzi](https://github.com/cashapp/paparazzi), to test UI pixel perfect. To add test,
|
||||
- Combination of [Paparazzi](https://github.com/cashapp/paparazzi) and
|
||||
[ComposablePreviewScanner](https://github.com/sergio-sastre/ComposablePreviewScanner), to test UI pixel perfect. To add test,
|
||||
just add `@Preview` for the composable you are adding. See the related [documentation](screenshot_testing.md) and see in the template the
|
||||
file [TemplateView.kt](../features/template/src/main/kotlin/io/element/android/features/template/TemplateView.kt). We create PreviewParam classes to provide
|
||||
different states. See for instance the
|
||||
@@ -449,9 +450,8 @@ number at the bottom 7 times. After tapping, a new "Developer options" entry wil
|
||||
at the bottom of the list.
|
||||
|
||||
The developer options include feature flags, notification/push history, Element call
|
||||
customization, Rust SDK log levels, per-feature tracing toggles, Showkase to debug UI
|
||||
components, rageshake controls, app crash controls, cache details/controls, persistent
|
||||
storage maintenance tasks.
|
||||
customization, Rust SDK log levels, per-feature tracing toggles, rageshake controls, app
|
||||
crash controls, cache details/controls, persistent storage maintenance tasks.
|
||||
|
||||
Keywords: Developer settings, developer mode
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ The CI checks that:
|
||||
2. The tests are passing
|
||||
3. The code quality is good (detekt, ktlint, lint)
|
||||
4. The code is running and smoke tests are passing (maestro)
|
||||
5. The PullRequest itself is good (with danger)
|
||||
5. The PullRequest itself is good (see the [PR Checks workflow](../.github/workflows/pr-checks.yml))
|
||||
6. Files that must be added with git-lfs are added with git-lfs
|
||||
|
||||
## What is the CI reporting
|
||||
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
## Danger
|
||||
|
||||
<!--- TOC -->
|
||||
|
||||
* [What does danger checks](#what-does-danger-checks)
|
||||
* [PR check](#pr-check)
|
||||
* [Quality check](#quality-check)
|
||||
* [Setup](#setup)
|
||||
* [Run danger locally](#run-danger-locally)
|
||||
* [Danger user](#danger-user)
|
||||
* [Useful links](#useful-links)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
## What does danger checks
|
||||
|
||||
### PR check
|
||||
|
||||
See the [dangerfile](../tools/danger/dangerfile.js). If you add rules in the dangerfile, please update the list below!
|
||||
|
||||
Here are the checks that Danger does so far:
|
||||
|
||||
- PR description is not empty
|
||||
- Big PR got a warning to recommend to split
|
||||
- PR contains a correct title and a label to categorize the release note
|
||||
- PR does not modify frozen classes
|
||||
- PR with change on layout should include screenshot in the description (TODO Not supported yet!)
|
||||
- PR which adds png file warn about the usage of vector drawables
|
||||
- non draft PR should have a reviewer
|
||||
- files containing translations are not modified by developers
|
||||
|
||||
### Quality check
|
||||
|
||||
After all the checks that generate checkstyle XML report, such as Ktlint, lint, or Detekt, Danger is run with this [dangerfile](../tools/danger/dangerfile-lint.js), in order to post comments to the PR with the detected error and warnings.
|
||||
|
||||
To run locally, you will have to install the plugin `danger-plugin-lint-report` using:
|
||||
|
||||
```shell
|
||||
yarn add danger-plugin-lint-report --dev
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
This operation should not be necessary, since Danger is already setup for the project.
|
||||
|
||||
To setup danger to the project, run:
|
||||
|
||||
```shell
|
||||
bundle exec danger init
|
||||
```
|
||||
|
||||
## Run danger locally
|
||||
|
||||
When modifying the [dangerfile](../tools/danger/dangerfile.js), you can check it by running Danger locally.
|
||||
|
||||
To run danger locally, install it and run:
|
||||
|
||||
```shell
|
||||
bundle exec danger pr <PR_URL> --dangerfile=./tools/danger/dangerfile.js
|
||||
```
|
||||
|
||||
For instance:
|
||||
|
||||
```shell
|
||||
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:
|
||||
|
||||
```shell
|
||||
export DANGER_GITHUB_API_TOKEN='YOUR_TOKEN'
|
||||
```
|
||||
|
||||
Swift and Kotlin (just in case)
|
||||
|
||||
```shell
|
||||
bundle exec danger-swift pr <PR_URL> --dangerfile=./tools/danger/dangerfile.js
|
||||
bundle exec danger-kotlin pr <PR_URL> --dangerfile=./tools/danger/dangerfile.js
|
||||
```
|
||||
|
||||
## Danger user
|
||||
|
||||
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/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.
|
||||
|
||||
## Useful links
|
||||
|
||||
- https://danger.systems/
|
||||
- https://danger.systems/js/
|
||||
- https://danger.systems/js/guides/getting_started.html
|
||||
- https://danger.systems/js/reference.html
|
||||
- https://github.com/danger/awesome-danger
|
||||
|
||||
Some danger files to get inspired from
|
||||
|
||||
- https://github.com/artsy/emission/blob/master/dangerfile.ts
|
||||
- https://github.com/facebook/react-native/blob/master/bots/dangerfile.js
|
||||
- https://github.com/apollographql/apollo-client/blob/master/config/dangerfile.ts
|
||||
- https://github.com/styleguidist/react-styleguidist/blob/master/dangerfile.js
|
||||
- https://github.com/storybooks/storybook/blob/master/dangerfile.js
|
||||
- https://github.com/ReactiveX/rxjs/blob/master/dangerfile.js
|
||||
+1
-1
Submodule enterprise updated: 53750903f2...bb07b4f839
@@ -0,0 +1,2 @@
|
||||
Main changes in this version: bug fixes and improvements.
|
||||
Full changelog: https://github.com/element-hq/element-x-android/releases
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_analytics_settings_help_us_improve">"提供匿名的使用數據以協助我們釐清問題。"</string>
|
||||
<string name="screen_analytics_settings_help_us_improve">"提供匿名的使用資料以協助我們釐清問題。"</string>
|
||||
<string name="screen_analytics_settings_read_terms">"您可以到%1$s閱讀我們的條款。"</string>
|
||||
<string name="screen_analytics_settings_read_terms_content_link">"這裡"</string>
|
||||
<string name="screen_analytics_settings_share_data">"提供分析數據"</string>
|
||||
<string name="screen_analytics_settings_share_data">"提供分析資料"</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_analytics_prompt_data_usage">"Wir speichern oder profilieren keine personenbezogenen Daten."</string>
|
||||
<string name="screen_analytics_prompt_data_usage">"Wir speichern oder profilieren keine personenbezogenen Daten"</string>
|
||||
<string name="screen_analytics_prompt_help_us_improve">"Teile anonyme Nutzungsdaten, um uns bei der Identifizierung von Problemen zu helfen."</string>
|
||||
<string name="screen_analytics_prompt_read_terms">"Weitere Informationen findest du %1$s."</string>
|
||||
<string name="screen_analytics_prompt_read_terms_content_link">"hier"</string>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_analytics_prompt_data_usage">"我們不會紀錄或剖繪您的個人資料"</string>
|
||||
<string name="screen_analytics_prompt_help_us_improve">"提供匿名的使用數據以協助我們釐清問題。"</string>
|
||||
<string name="screen_analytics_prompt_help_us_improve">"提供匿名的使用資料以協助我們釐清問題。"</string>
|
||||
<string name="screen_analytics_prompt_read_terms">"您可以到%1$s閱讀我們的條款。"</string>
|
||||
<string name="screen_analytics_prompt_read_terms_content_link">"這裡"</string>
|
||||
<string name="screen_analytics_prompt_settings">"您可以在任何時候關閉它"</string>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="call_foreground_service_channel_title_android">"Połączenie w trakcie"</string>
|
||||
<string name="call_foreground_service_message_android">"Stuknij, aby wrócić do rozmowy"</string>
|
||||
<string name="call_foreground_service_message_android">"Dotknij, aby wrócić do rozmowy"</string>
|
||||
<string name="call_foreground_service_title_android">"☎️ Rozmowa w toku"</string>
|
||||
<string name="call_invalid_audio_device_bluetooth_devices_disabled">"Element Call nie obsługuje korzystania z urządzeń audio Bluetooth w tej wersji Androida. Wybierz inne urządzenie audio."</string>
|
||||
<string name="screen_incoming_call_subtitle_android">"Przychodzące połączenie Element"</string>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_create_room_action_create_room">"Neuer Chat"</string>
|
||||
<string name="screen_create_room_add_people_title">"Nutzer einladen"</string>
|
||||
<string name="screen_create_room_add_people_title">"Personen einladen"</string>
|
||||
<string name="screen_create_room_error_creating_room">"Beim Erstellen des Chats ist ein Fehler aufgetreten"</string>
|
||||
<string name="screen_create_room_error_creating_space">"Der Space konnte wegen eines unbekannten Fehlers nicht erstellt werden. Versuch\' es später nochmal."</string>
|
||||
<string name="screen_create_room_error_creating_space">"Der Space konnte wegen eines unbekannten Fehlers nicht erstellt werden. Bitte versuche es später erneut."</string>
|
||||
<string name="screen_create_room_name_placeholder">"Name hinzufügen…"</string>
|
||||
<string name="screen_create_room_new_room_title">"Neuer Chat"</string>
|
||||
<string name="screen_create_room_new_room_title">"Neue Gruppe"</string>
|
||||
<string name="screen_create_room_new_space_title">"Neuer Space"</string>
|
||||
<string name="screen_create_room_private_option_description">"Nur eingeladene Personen haben Zutritt zu diesem Chat."</string>
|
||||
<string name="screen_create_room_private_option_title">"Privat"</string>
|
||||
<string name="screen_create_room_public_option_description">"Jeder kann diesen Chat finden.
|
||||
Du kannst dies jederzeit in den Einstellungen des Chats ändern."</string>
|
||||
<string name="screen_create_room_public_option_short_description">"Jeder kann beitreten."</string>
|
||||
<string name="screen_create_room_public_option_short_description">"Alle können beitreten."</string>
|
||||
<string name="screen_create_room_public_option_title">"Öffentlich"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_description">"Jeder kann den Beitritt zum Chat erbitten, aber ein Admin oder Moderator muss die Anfrage akzeptieren."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"Anfrage zum Beitritt zulassen"</string>
|
||||
@@ -19,18 +19,18 @@ Du kannst dies jederzeit in den Einstellungen des Chats ändern."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_restricted_option_title">"Beitritt anfragen"</string>
|
||||
<string name="screen_create_room_room_access_section_private_option_description">"Nur eingeladene Personen können beitreten."</string>
|
||||
<string name="screen_create_room_room_access_section_private_option_title">"Privat"</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_description">"Jeder darf diesem Chat beitreten."</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_description">"Alle können beitreten."</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_title">"Öffentlich"</string>
|
||||
<string name="screen_create_room_room_access_section_restricted_option_description">"Jeder in %1$s kann beitreten."</string>
|
||||
<string name="screen_create_room_room_access_section_restricted_option_title">"Standard"</string>
|
||||
<string name="screen_create_room_room_access_section_title">"Wer hat Zugang"</string>
|
||||
<string name="screen_create_room_room_address_section_footer">"Du benötigst eine Adresse, um diesen Chat im öffentlichen Verzeichnis sichtbar zu machen."</string>
|
||||
<string name="screen_create_room_room_address_section_title">"Adresse"</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">" Sichtbarkeit des Chats"</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Sichtbarkeit des Chats"</string>
|
||||
<string name="screen_create_room_space_selection_no_space_description">"(kein Space)"</string>
|
||||
<string name="screen_create_room_space_selection_no_space_option">"Nicht zu einem Space hinzufügen"</string>
|
||||
<string name="screen_create_room_space_selection_no_space_title">"Kein Space ausgewählt"</string>
|
||||
<string name="screen_create_room_space_selection_sheet_title">"Space hinzufügen"</string>
|
||||
<string name="screen_create_room_space_selection_sheet_title">"Zu Space hinzufügen"</string>
|
||||
<string name="screen_create_room_topic_label">"Thema (optional)"</string>
|
||||
<string name="screen_create_room_topic_placeholder">"Beschreibung hinzufügen…"</string>
|
||||
</resources>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<string name="screen_create_room_private_option_title">"Prywatny"</string>
|
||||
<string name="screen_create_room_public_option_description">"Każdy może znaleźć ten pokój.
|
||||
Możesz to zmienić w ustawieniach pokoju."</string>
|
||||
<string name="screen_create_room_public_option_short_description">"Każdy może dołączyć."</string>
|
||||
<string name="screen_create_room_public_option_short_description">"Każdy może dołączyć, w tym osoby spoza Twojej organizacji."</string>
|
||||
<string name="screen_create_room_public_option_title">"Publiczny"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_description">"Każdy może poprosić o dołączenie, ale administrator lub moderator musi to zaakceptować."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"Zezwól na prośbę o dołączenie"</string>
|
||||
@@ -19,7 +19,7 @@ Możesz to zmienić w ustawieniach pokoju."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_restricted_option_title">"Poproś o dołączenie"</string>
|
||||
<string name="screen_create_room_room_access_section_private_option_description">"Dołączyć mogą tylko zaproszone osoby."</string>
|
||||
<string name="screen_create_room_room_access_section_private_option_title">"Prywatny"</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_description">"Każdy może dołączyć."</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_description">"Każdy może dołączyć, w tym osoby spoza Twojej organizacji."</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_title">"Publiczny"</string>
|
||||
<string name="screen_create_room_room_access_section_restricted_option_description">"Każdy w %1$s może dołączyć."</string>
|
||||
<string name="screen_create_room_room_access_section_restricted_option_title">"Standardowy"</string>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<string name="screen_create_room_private_option_title">"私密"</string>
|
||||
<string name="screen_create_room_public_option_description">"任何人都能找到此房间。
|
||||
你可以随时在房间设置中更改。"</string>
|
||||
<string name="screen_create_room_public_option_short_description">"任何人都可以加入"</string>
|
||||
<string name="screen_create_room_public_option_short_description">"任何人都可以加入,包括你的组织之外的人员。"</string>
|
||||
<string name="screen_create_room_public_option_title">"公共"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_description">"任何人都可申请加入,但需由管理员或协管员批准申请。"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"申请加入"</string>
|
||||
@@ -19,7 +19,7 @@
|
||||
<string name="screen_create_room_room_access_section_knocking_restricted_option_title">"申请加入"</string>
|
||||
<string name="screen_create_room_room_access_section_private_option_description">"仅限受邀人员加入。"</string>
|
||||
<string name="screen_create_room_room_access_section_private_option_title">"私密"</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_description">"任何人都可以加入。"</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_description">"任何人都可以加入,包括你的组织之外的人员。"</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_title">"公共"</string>
|
||||
<string name="screen_create_room_room_access_section_restricted_option_description">"%1$s 中的任何人都可以加入。"</string>
|
||||
<string name="screen_create_room_room_access_section_restricted_option_title">"标准"</string>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<string name="screen_create_room_private_option_title">"Private"</string>
|
||||
<string name="screen_create_room_public_option_description">"Anyone can find this room.
|
||||
You can change this anytime in room settings."</string>
|
||||
<string name="screen_create_room_public_option_short_description">"Anyone can join."</string>
|
||||
<string name="screen_create_room_public_option_short_description">"Anyone can join, including people outside your organisation."</string>
|
||||
<string name="screen_create_room_public_option_title">"Public"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_description">"Anyone can ask to join but an administrator or a moderator must accept the request."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"Allow ask to join"</string>
|
||||
@@ -19,7 +19,7 @@ You can change this anytime in room settings."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_restricted_option_title">"Ask to join"</string>
|
||||
<string name="screen_create_room_room_access_section_private_option_description">"Only people invited can join."</string>
|
||||
<string name="screen_create_room_room_access_section_private_option_title">"Private"</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_description">"Anyone can join."</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_description">"Anyone can join, including people outside your organisation."</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_title">"Public"</string>
|
||||
<string name="screen_create_room_room_access_section_restricted_option_description">"Anyone in %1$s can join."</string>
|
||||
<string name="screen_create_room_room_access_section_restricted_option_title">"Standard"</string>
|
||||
|
||||
@@ -10,6 +10,7 @@ import extension.testCommonDependencies
|
||||
*/
|
||||
plugins {
|
||||
id("io.element.android-compose-library")
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
}
|
||||
|
||||
android {
|
||||
@@ -22,8 +23,10 @@ dependencies {
|
||||
implementation(projects.libraries.compound)
|
||||
api(projects.features.enterprise.api)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.androidutils)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.wellknown.api)
|
||||
implementation(libs.serialization.json)
|
||||
|
||||
testCommonDependencies(libs)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
|
||||
+35
-2
@@ -14,18 +14,43 @@ import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.compound.colors.SemanticColorsLightDark
|
||||
import io.element.android.features.enterprise.api.BugReportUrl
|
||||
import io.element.android.features.enterprise.api.EnterpriseService
|
||||
import io.element.android.libraries.androidutils.json.JsonProvider
|
||||
import io.element.android.libraries.core.extensions.mapCatchingExceptions
|
||||
import io.element.android.libraries.core.uri.ensureProtocol
|
||||
import io.element.android.libraries.matrix.api.ClientUrlContentFetcher
|
||||
import io.element.android.libraries.matrix.api.TemporaryMatrixClientFactory
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import timber.log.Timber
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultEnterpriseService : EnterpriseService {
|
||||
class DefaultEnterpriseService(
|
||||
private val temporaryMatrixClientFactory: TemporaryMatrixClientFactory,
|
||||
private val jsonProvider: JsonProvider,
|
||||
) : EnterpriseService {
|
||||
override suspend fun isEnterpriseUser(sessionId: SessionId) = false
|
||||
override suspend fun tweakMasUrl(url: String, urlContentFetcher: ClientUrlContentFetcher) = url
|
||||
override fun homeserverAllowList(): List<String> = emptyList()
|
||||
override suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String) = true
|
||||
override suspend fun isElementProEnforced(serverName: String): Boolean = false
|
||||
override suspend fun isElementProEnforced(serverName: String): Boolean {
|
||||
val temporaryMatrixClient = temporaryMatrixClientFactory.create(serverName).getOrElse { return false }
|
||||
return temporaryMatrixClient.use { client ->
|
||||
val baseUrl = serverName.ensureProtocol().removeSuffix("/")
|
||||
// We'll always perform a network request here since we're not interested in any cached value.
|
||||
client.getUrl("$baseUrl/.well-known/element/element.json")
|
||||
.mapCatchingExceptions { response ->
|
||||
val remoteConfig = jsonProvider().decodeFromString<MinimalEnterpriseConfig>(String(response))
|
||||
remoteConfig.enforceElementPro ?: false
|
||||
}
|
||||
.onFailure {
|
||||
Timber.e(it, "Failed to fetch enterprise config for checking if Element Pro is enforced for $serverName")
|
||||
}
|
||||
.getOrElse { false }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun overrideBrandColor(sessionId: SessionId?, brandColor: String?) = Unit
|
||||
|
||||
@@ -46,3 +71,11 @@ class DefaultEnterpriseService : EnterpriseService {
|
||||
|
||||
override fun getNoisyNotificationChannelId(sessionId: SessionId): String? = null
|
||||
}
|
||||
|
||||
/**
|
||||
* A minimal version of the enterprise config that only contains the fields we need to check if Element Pro is enforced.
|
||||
*/
|
||||
@Serializable
|
||||
private data class MinimalEnterpriseConfig(
|
||||
@SerialName("enforce_element_pro") val enforceElementPro: Boolean? = null,
|
||||
)
|
||||
|
||||
+45
-11
@@ -12,33 +12,40 @@ import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.compound.colors.SemanticColorsLightDark
|
||||
import io.element.android.features.enterprise.api.BugReportUrl
|
||||
import io.element.android.libraries.androidutils.json.JsonProvider
|
||||
import io.element.android.libraries.core.uri.ensureProtocol
|
||||
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.FakeTemporaryMatrixClient
|
||||
import io.element.android.libraries.matrix.test.FakeTemporaryMatrixClientFactory
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultEnterpriseServiceTest {
|
||||
@Test
|
||||
fun homeserverWhitelist() {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
assertThat(defaultEnterpriseService.homeserverAllowList()).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isAllowedToConnectToHomeserver is true for all homeserver urls`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
assertThat(defaultEnterpriseService.isAllowedToConnectToHomeserver(A_HOMESERVER_URL)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isEnterpriseUser always return false`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
assertThat(defaultEnterpriseService.isEnterpriseUser(A_SESSION_ID)).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `semanticColorsFlow always emits the same value`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
defaultEnterpriseService.semanticColorsFlow(null).test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState).isEqualTo(SemanticColorsLightDark.default)
|
||||
@@ -48,7 +55,7 @@ class DefaultEnterpriseServiceTest {
|
||||
|
||||
@Test
|
||||
fun `brandColorsFlow always emits null`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
defaultEnterpriseService.brandColorsFlow(null).test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState).isNull()
|
||||
@@ -58,7 +65,7 @@ class DefaultEnterpriseServiceTest {
|
||||
|
||||
@Test
|
||||
fun `semanticColorsFlow always emits the same value for a session`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
defaultEnterpriseService.semanticColorsFlow(A_SESSION_ID).test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState).isEqualTo(SemanticColorsLightDark.default)
|
||||
@@ -68,25 +75,25 @@ class DefaultEnterpriseServiceTest {
|
||||
|
||||
@Test
|
||||
fun `overrideBrandColor has no effect`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
defaultEnterpriseService.overrideBrandColor(A_SESSION_ID, "aColor")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `firebasePushGateway returns null`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
assertThat(defaultEnterpriseService.firebasePushGateway()).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unifiedPushDefaultPushGateway returns null`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
assertThat(defaultEnterpriseService.unifiedPushDefaultPushGateway()).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `bugReportUrlFlow only emits UseDefault`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
defaultEnterpriseService.bugReportUrlFlow(A_SESSION_ID).test {
|
||||
assertThat(awaitItem()).isEqualTo(BugReportUrl.UseDefault)
|
||||
awaitComplete()
|
||||
@@ -95,7 +102,34 @@ class DefaultEnterpriseServiceTest {
|
||||
|
||||
@Test
|
||||
fun `getNoisyNotificationChannelId returns null`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
assertThat(defaultEnterpriseService.getNoisyNotificationChannelId(A_SESSION_ID)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isElementProEnforced checks using a temporary client`() = runTest {
|
||||
val closeLambda = lambdaRecorder<Unit> {}
|
||||
val getUrlLambda = lambdaRecorder<String, Result<ByteArray>> {
|
||||
Result.success("""{"enforce_element_pro": true}""".toByteArray())
|
||||
}
|
||||
val client = FakeTemporaryMatrixClient(
|
||||
getUrlResult = getUrlLambda,
|
||||
closeLambda = closeLambda,
|
||||
)
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService(client = client)
|
||||
assertThat(defaultEnterpriseService.isElementProEnforced(A_HOMESERVER_URL)).isTrue()
|
||||
|
||||
// Verify that the temporary client was used to fetch the URL and then closed
|
||||
val expectedUrl = "${A_HOMESERVER_URL.ensureProtocol()}/.well-known/element/element.json"
|
||||
getUrlLambda.assertions().isCalledOnce().with(value(expectedUrl))
|
||||
closeLambda.assertions().isCalledOnce()
|
||||
}
|
||||
|
||||
private fun createDefaultEnterpriseService(
|
||||
client: FakeTemporaryMatrixClient = FakeTemporaryMatrixClient(),
|
||||
jsonProvider: JsonProvider = { Json }
|
||||
) = DefaultEnterpriseService(
|
||||
temporaryMatrixClientFactory = FakeTemporaryMatrixClientFactory(createResult = { Result.success(client) }),
|
||||
jsonProvider = jsonProvider,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
<string name="banner_battery_optimization_title_android">"Kommen die Benachrichtigungen nicht an?"</string>
|
||||
<string name="banner_new_sound_message">"Dein Benachrichtigungs-Ping wurde aktualisiert – klarer, schneller und weniger störend."</string>
|
||||
<string name="banner_new_sound_title">"Wir haben deine Sounds aktualisiert"</string>
|
||||
<string name="banner_set_up_recovery_content">"Deine Chats werden automatisch gesichert und mit einer Ende-zu-Ende-Verschlüsselung geschützt. Um dieses Backup wiederherzustellen und deine digitale Identität zu bewahren, falls du den Zugriff auf alle deine Geräte verlierst, benötigst du deinen Wiederherstellungsschlüssel."</string>
|
||||
<string name="banner_set_up_recovery_submit">"Wiederherstellungsschlüssel einrichten"</string>
|
||||
<string name="banner_set_up_recovery_title">"Sichere deine Chats"</string>
|
||||
<string name="banner_set_up_recovery_content">"Von deinen Chats wird automatisch ein Ende-zu-Ende-verschlüsseltes Backup erstellt. Wenn du den Zugriff auf alle deine Geräte verlierst, brauchst du deinen Wiederherstellungsschlüssel, um dieses Backup wiederherzustellen und deine digitale Identität zu behalten."</string>
|
||||
<string name="banner_set_up_recovery_submit">"Wiederherstellungsschlüssel erstellen"</string>
|
||||
<string name="banner_set_up_recovery_title">"Deine Chats sichern"</string>
|
||||
<string name="confirm_recovery_key_banner_message">"Bestätige deinen Wiederherstellungsschlüssel, um weiterhin auf deinen Schlüsselspeicher und den Nachrichtenverlauf zugreifen zu können."</string>
|
||||
<string name="confirm_recovery_key_banner_primary_button_title">"Gib deinen Wiederherstellungsschlüssel ein"</string>
|
||||
<string name="confirm_recovery_key_banner_secondary_button_title">"Hast du deinen Wiederherstellungsschlüssel vergessen?"</string>
|
||||
@@ -16,7 +16,7 @@
|
||||
<string name="full_screen_intent_banner_title">"Verbessere dein Anruferlebnis"</string>
|
||||
<string name="screen_home_tab_chats">"Chats"</string>
|
||||
<string name="screen_home_tab_spaces">"Spaces"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Möchtest du die Einladung zum Betreten von %1$s wirklich ablehnen?"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Möchtest du die Einladung zu %1$s wirklich ablehnen?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Einladung ablehnen"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Bist du sicher, dass du diese Direktnachricht von %1$s ablehnen möchtest?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Einladung ablehnen"</string>
|
||||
@@ -35,9 +35,9 @@ Deaktiviere den entsprechenden Filter, um deine anderen Chats zu sehen"</string>
|
||||
<string name="screen_roomlist_filter_invites">"Einladungen"</string>
|
||||
<string name="screen_roomlist_filter_invites_empty_state_title">"Du hast keine ausstehenden Einladungen."</string>
|
||||
<string name="screen_roomlist_filter_low_priority">"Niedrige Priorität"</string>
|
||||
<string name="screen_roomlist_filter_low_priority_empty_state_title">"Du hast noch keine Chats mit niedriger Priorität."</string>
|
||||
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Wähle Filter ab, um Chats zu sehen."</string>
|
||||
<string name="screen_roomlist_filter_mixed_empty_state_title">"Für diese Auswahl hast du keinen Chat."</string>
|
||||
<string name="screen_roomlist_filter_low_priority_empty_state_title">"Du hast noch keine Chats mit niedriger Priorität"</string>
|
||||
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Entferne Filter, um Chats zu sehen"</string>
|
||||
<string name="screen_roomlist_filter_mixed_empty_state_title">"Für diese Auswahl hast du keinen Chat"</string>
|
||||
<string name="screen_roomlist_filter_people">"Personen"</string>
|
||||
<string name="screen_roomlist_filter_people_empty_state_title">"Du hast noch keine Direktnachrichten"</string>
|
||||
<string name="screen_roomlist_filter_rooms">"Gruppen"</string>
|
||||
@@ -45,7 +45,7 @@ Deaktiviere den entsprechenden Filter, um deine anderen Chats zu sehen"</string>
|
||||
<string name="screen_roomlist_filter_unreads">"Ungelesen"</string>
|
||||
<string name="screen_roomlist_filter_unreads_empty_state_title">"Glückwunsch!
|
||||
Du hast keine ungelesenen Nachrichten!"</string>
|
||||
<string name="screen_roomlist_knock_event_sent_description">"Beitrittsanfrage geschickt"</string>
|
||||
<string name="screen_roomlist_knock_event_sent_description">"Beitrittsanfrage gestellt"</string>
|
||||
<string name="screen_roomlist_main_space_title">"Chats"</string>
|
||||
<string name="screen_roomlist_mark_as_read">"Als gelesen markieren"</string>
|
||||
<string name="screen_roomlist_mark_as_unread">"Als ungelesen markieren"</string>
|
||||
|
||||
@@ -36,6 +36,8 @@ Zasad možete poništiti odabir filtara kako biste vidjeli ostale razgovore."</s
|
||||
<string name="screen_roomlist_filter_invites_empty_state_title">"Nemate pozivnica na čekanju."</string>
|
||||
<string name="screen_roomlist_filter_low_priority">"Nizak prioritet"</string>
|
||||
<string name="screen_roomlist_filter_low_priority_empty_state_title">"Još nemate razgovora niskog prioriteta"</string>
|
||||
<string name="screen_roomlist_filter_mention">"Spomeni"</string>
|
||||
<string name="screen_roomlist_filter_mention_empty_state_title">"Nemate nepročitanih spominjanja"</string>
|
||||
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Možete poništiti odabir filtara kako biste vidjeli ostale razgovore"</string>
|
||||
<string name="screen_roomlist_filter_mixed_empty_state_title">"Nemate razgovora za ovaj odabir"</string>
|
||||
<string name="screen_roomlist_filter_people">"Osobe"</string>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<string name="confirm_recovery_key_banner_title">"L\'archiviazione delle chiavi non è sincronizzata"</string>
|
||||
<string name="full_screen_intent_banner_message">"Per non perdere mai una chiamata importante, modifica le impostazioni per consentire le notifiche a schermo intero quando il telefono è bloccato."</string>
|
||||
<string name="full_screen_intent_banner_title">"Migliora la tua esperienza di chiamata"</string>
|
||||
<string name="screen_home_tab_chats">"Tutte le conversazioni"</string>
|
||||
<string name="screen_home_tab_chats">"Conversazioni"</string>
|
||||
<string name="screen_home_tab_spaces">"Spazi"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Vuoi davvero rifiutare l\'invito ad entrare in %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Rifiuta l\'invito"</string>
|
||||
@@ -46,7 +46,7 @@ Per il momento, puoi deselezionare i filtri per vedere le altre conversazioni."<
|
||||
<string name="screen_roomlist_filter_unreads_empty_state_title">"Congratulazioni!
|
||||
Non hai messaggi non letti!"</string>
|
||||
<string name="screen_roomlist_knock_event_sent_description">"Richiesta di accesso inviata"</string>
|
||||
<string name="screen_roomlist_main_space_title">"Tutte le conversazioni"</string>
|
||||
<string name="screen_roomlist_main_space_title">"Conversazioni"</string>
|
||||
<string name="screen_roomlist_mark_as_read">"Segna come letto"</string>
|
||||
<string name="screen_roomlist_mark_as_unread">"Segna come non letto"</string>
|
||||
<string name="screen_roomlist_tombstoned_room_description">"Questa stanza è stata aggiornata"</string>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<string name="confirm_recovery_key_banner_title">"Magazyn kluczy nie jest zsynchronizowany"</string>
|
||||
<string name="full_screen_intent_banner_message">"Upewnij się, że nie pominiesz żadnego połączenia. Zmień swoje ustawienia i zezwól na powiadomienia na blokadzie ekranu."</string>
|
||||
<string name="full_screen_intent_banner_title">"Popraw jakość swoich rozmów"</string>
|
||||
<string name="screen_home_tab_chats">"Wszystkie czaty"</string>
|
||||
<string name="screen_home_tab_chats">"Czaty"</string>
|
||||
<string name="screen_home_tab_spaces">"Przestrzenie"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Czy na pewno chcesz odrzucić zaproszenie dołączenia do %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Odrzuć zaproszenie"</string>
|
||||
@@ -36,6 +36,8 @@ Na razie możesz wyczyścić filtry, aby zobaczyć pozostałe czaty"</string>
|
||||
<string name="screen_roomlist_filter_invites_empty_state_title">"Nie masz żadnych oczekujących zaproszeń."</string>
|
||||
<string name="screen_roomlist_filter_low_priority">"Niski priorytet"</string>
|
||||
<string name="screen_roomlist_filter_low_priority_empty_state_title">"Nie masz jeszcze żadnych czatów o niskim priorytecie"</string>
|
||||
<string name="screen_roomlist_filter_mention">"Wzmianka"</string>
|
||||
<string name="screen_roomlist_filter_mention_empty_state_title">"Nie masz żadnych nieprzeczytanych wzmianek"</string>
|
||||
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Wyczyść filtry, aby zobaczyć pozostałe czaty"</string>
|
||||
<string name="screen_roomlist_filter_mixed_empty_state_title">"Brak czatów dla podanych kryteriów"</string>
|
||||
<string name="screen_roomlist_filter_people">"Osoby"</string>
|
||||
@@ -46,7 +48,7 @@ Na razie możesz wyczyścić filtry, aby zobaczyć pozostałe czaty"</string>
|
||||
<string name="screen_roomlist_filter_unreads_empty_state_title">"Gratulacje!
|
||||
Nie masz żadnych nieprzeczytanych wiadomości!"</string>
|
||||
<string name="screen_roomlist_knock_event_sent_description">"Wysłano prośbę o dołączenie"</string>
|
||||
<string name="screen_roomlist_main_space_title">"Wszystkie czaty"</string>
|
||||
<string name="screen_roomlist_main_space_title">"Czaty"</string>
|
||||
<string name="screen_roomlist_mark_as_read">"Oznacz jako przeczytane"</string>
|
||||
<string name="screen_roomlist_mark_as_unread">"Oznacz jako nieprzeczytane"</string>
|
||||
<string name="screen_roomlist_tombstoned_room_description">"Ten pokój został ulepszony"</string>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<string name="confirm_recovery_key_banner_title">"Backup-ul nu este sincronizat"</string>
|
||||
<string name="full_screen_intent_banner_message">"Pentru a vă asigura că nu pierdeți niciodată un apel important, vă rugăm să modificați setările pentru a permite notificări fullscreen atunci când telefonul este blocat."</string>
|
||||
<string name="full_screen_intent_banner_title">"Îmbunătățiți-vă experiența in timpul unui apel"</string>
|
||||
<string name="screen_home_tab_chats">"Toate conversatiile"</string>
|
||||
<string name="screen_home_tab_chats">"Conversații"</string>
|
||||
<string name="screen_home_tab_spaces">"Spații"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Sigur doriți să refuzați alăturarea la %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Refuzați invitația"</string>
|
||||
@@ -46,7 +46,7 @@ Deocamdată, puteți deselecta filtrele pentru a vedea celelalte chat-uri"</stri
|
||||
<string name="screen_roomlist_filter_unreads_empty_state_title">"Felicitari!
|
||||
Nu aveți mesaje necitite!"</string>
|
||||
<string name="screen_roomlist_knock_event_sent_description">"Cererea de alăturare a fost trimisă"</string>
|
||||
<string name="screen_roomlist_main_space_title">"Toate conversatiile"</string>
|
||||
<string name="screen_roomlist_main_space_title">"Conversații"</string>
|
||||
<string name="screen_roomlist_mark_as_read">"Marcați ca citită"</string>
|
||||
<string name="screen_roomlist_mark_as_unread">"Marcați ca necitită"</string>
|
||||
<string name="screen_roomlist_tombstoned_room_description">"Această cameră a fost modernizată."</string>
|
||||
|
||||
@@ -36,13 +36,15 @@
|
||||
<string name="screen_roomlist_filter_invites_empty_state_title">"У Вас нет приглашений."</string>
|
||||
<string name="screen_roomlist_filter_low_priority">"Низкий приоритет"</string>
|
||||
<string name="screen_roomlist_filter_low_priority_empty_state_title">"У Вас пока нет чатов с низким приоритетом."</string>
|
||||
<string name="screen_roomlist_filter_mention">"Упоминания"</string>
|
||||
<string name="screen_roomlist_filter_mention_empty_state_title">"У Вас нет непрочитанных упоминаний"</string>
|
||||
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Вы можете убрать фильтры, чтобы увидеть другие ваши чаты."</string>
|
||||
<string name="screen_roomlist_filter_mixed_empty_state_title">"У Вас нет чатов, соответствующих фильтрам"</string>
|
||||
<string name="screen_roomlist_filter_people">"Пользователи"</string>
|
||||
<string name="screen_roomlist_filter_people_empty_state_title">"У Вас пока нет личных сообщений"</string>
|
||||
<string name="screen_roomlist_filter_rooms">"Комнаты"</string>
|
||||
<string name="screen_roomlist_filter_rooms_empty_state_title">"Вас пока нет ни в одной комнате"</string>
|
||||
<string name="screen_roomlist_filter_unreads">"Новые"</string>
|
||||
<string name="screen_roomlist_filter_unreads">"Непрочитанные"</string>
|
||||
<string name="screen_roomlist_filter_unreads_empty_state_title">"Поздравляем!
|
||||
У Вас нет непрочитанных сообщений!"</string>
|
||||
<string name="screen_roomlist_knock_event_sent_description">"Запрос на присоединение отправлен"</string>
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
<string name="screen_roomlist_filter_invites_empty_state_title">"У вас немає запрошень, що очікують на розгляд."</string>
|
||||
<string name="screen_roomlist_filter_low_priority">"Низький пріоритет"</string>
|
||||
<string name="screen_roomlist_filter_low_priority_empty_state_title">"У вас ще немає неважливих бесід"</string>
|
||||
<string name="screen_roomlist_filter_mention">"Згадка"</string>
|
||||
<string name="screen_roomlist_filter_mention_empty_state_title">"У вас немає непрочитаних згадок"</string>
|
||||
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Ви можете зняти фільтри, щоб побачити інші ваші бесіди"</string>
|
||||
<string name="screen_roomlist_filter_mixed_empty_state_title">"Ви не маєте бесід для цієї категорії"</string>
|
||||
<string name="screen_roomlist_filter_people">"Люди"</string>
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
<string name="screen_roomlist_filter_invites_empty_state_title">"没有待处理的邀请。"</string>
|
||||
<string name="screen_roomlist_filter_low_priority">"低优先级"</string>
|
||||
<string name="screen_roomlist_filter_low_priority_empty_state_title">"你暂无任何低优先级聊天"</string>
|
||||
<string name="screen_roomlist_filter_mention">"提及"</string>
|
||||
<string name="screen_roomlist_filter_mention_empty_state_title">"你没有任何未读提及"</string>
|
||||
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"你可以取消选择筛选器以查看其它对话"</string>
|
||||
<string name="screen_roomlist_filter_mixed_empty_state_title">"你暂无适用于此选项的聊天"</string>
|
||||
<string name="screen_roomlist_filter_people">"人员"</string>
|
||||
|
||||
@@ -36,13 +36,15 @@ For now, you can deselect filters in order to see your other chats"</string>
|
||||
<string name="screen_roomlist_filter_invites_empty_state_title">"You don\'t have any pending invites."</string>
|
||||
<string name="screen_roomlist_filter_low_priority">"Low Priority"</string>
|
||||
<string name="screen_roomlist_filter_low_priority_empty_state_title">"You don’t have any low priority chats yet"</string>
|
||||
<string name="screen_roomlist_filter_mention">"Mention"</string>
|
||||
<string name="screen_roomlist_filter_mention_empty_state_title">"You don’t have any unread mentions"</string>
|
||||
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"You can deselect filters in order to see your other chats"</string>
|
||||
<string name="screen_roomlist_filter_mixed_empty_state_title">"You don’t have chats for this selection"</string>
|
||||
<string name="screen_roomlist_filter_people">"People"</string>
|
||||
<string name="screen_roomlist_filter_people_empty_state_title">"You don’t have any DMs yet"</string>
|
||||
<string name="screen_roomlist_filter_rooms">"Rooms"</string>
|
||||
<string name="screen_roomlist_filter_rooms_empty_state_title">"You’re not in any room yet"</string>
|
||||
<string name="screen_roomlist_filter_unreads">"Unreads"</string>
|
||||
<string name="screen_roomlist_filter_unreads">"Unread"</string>
|
||||
<string name="screen_roomlist_filter_unreads_empty_state_title">"Congrats!
|
||||
You don’t have any unread messages!"</string>
|
||||
<string name="screen_roomlist_knock_event_sent_description">"Request to join sent"</string>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Du wirst keine Nachrichten oder Chat-Einladungen von diesem Nutzer sehen."</string>
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Du wirst keine Nachrichten oder Chat-Einladungen von diesem Nutzer sehen"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Nutzer blockieren"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Melde diesen Chat deinem Konto-Anbieter."</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Melde diesen Chat deinem Kontoanbieter."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Beschreibe den Grund für die Meldung…"</string>
|
||||
<string name="screen_decline_and_block_title">"Ablehnen und blockieren"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Möchtest du die Einladung zum Betreten von %1$s wirklich ablehnen?"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Möchtest du die Einladung zu %1$s wirklich ablehnen?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Einladung ablehnen"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Bist du sicher, dass du diese Direktnachricht von %1$s ablehnen möchtest?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Einladung ablehnen"</string>
|
||||
|
||||
@@ -2,4 +2,8 @@
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"Ήδη μέλος"</string>
|
||||
<string name="screen_invite_users_already_invited">"Ήδη προσκεκλημένος"</string>
|
||||
<string name="screen_invite_users_confirm_dialog_subtitle_multiple_users">"Προς το παρόν δεν έχετε καμία συνομιλία με αυτές τις επαφές. Επιβεβαιώστε την πρόσκλησή τους σε αυτήν την αίθουσα πριν συνεχίσετε."</string>
|
||||
<string name="screen_invite_users_confirm_dialog_subtitle_one_user">"Προς το παρόν δεν έχετε καμία συνομιλία με αυτήν την επαφή. Επιβεβαιώστε την πρόσκληση σε αυτήν την αίθουσα συνομιλίας πριν συνεχίσετε."</string>
|
||||
<string name="screen_invite_users_confirm_dialog_title_mutiple_users">"Πρόσκληση νέων επαφών σε αυτη την αίθουσα;"</string>
|
||||
<string name="screen_invite_users_confirm_dialog_title_one_user">"Θέλετε να προσκαλέσετε νέα επαφή σε αυτήν την αίθουσα;"</string>
|
||||
</resources>
|
||||
|
||||
@@ -22,12 +22,12 @@
|
||||
<string name="screen_join_room_knock_message_characters_count">"%1$d von %2$d erlaubte Zeichen"</string>
|
||||
<string name="screen_join_room_knock_message_description">"Nachricht (optional)"</string>
|
||||
<string name="screen_join_room_knock_sent_description">"Sollte deine Anfrage akzeptiert werden, erhältst du eine Einladung, dem Chat beizutreten."</string>
|
||||
<string name="screen_join_room_knock_sent_title">"Beitrittsanfrage geschickt"</string>
|
||||
<string name="screen_join_room_knock_sent_title">"Beitrittsanfrage gestellt"</string>
|
||||
<string name="screen_join_room_loading_alert_message">"Wir konnten die Chat Vorschau nicht anzeigen. Dies kann an Netzwerk- oder Serverproblemen liegen."</string>
|
||||
<string name="screen_join_room_loading_alert_title">"Wir konnten diese Chat-Vorschau nicht anzeigen"</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"%1$s unterstützt noch keine Spaces. Du kannst auf Spaces im Web zugreifen."</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"Spaces werden noch nicht unterstützt"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Klopfe an um einen Admin zu benachrichtigen. Nach der Freigabe kannst du dich an der Unterhaltung beteiligen."</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Wähle die Schaltfläche unten – eine Chat-Administration wird benachrichtigt. Nach der Freigabe kannst du dich an der Unterhaltung beteiligen."</string>
|
||||
<string name="screen_join_room_subtitle_no_preview">"Du musst Mitglied in diesem Chat sein, um den Nachrichtenverlauf zu sehen."</string>
|
||||
<string name="screen_join_room_title_knock">"Willst du diesem Chat beitreten?"</string>
|
||||
<string name="screen_join_room_title_no_preview">"Vorschau nicht verfügbar"</string>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<string name="screen_join_room_loading_alert_title">"Δεν μπορέσαμε να εμφανίσουμε αυτή την προεπισκόπηση αίθουσας"</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"Το %1$s δεν υποστηρίζει ακόμα χώρους. Μπορείς να έχεις πρόσβαση σε χώρους στον ιστό."</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"Οι Χώροι δεν υποστηρίζονται ακόμα"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Κάντε κλικ στο παρακάτω κουμπί και θα ειδοποιηθεί ένας διαχειριστής της αίθουσας. Μόλις εγκριθείτε, θα μπορείτε να συμμετάσχετε στη συζήτηση."</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Πατήστε το παρακάτω κουμπί και θα ειδοποιηθεί ένας διαχειριστής της συνομιλίας. Θα μπορείτε να συμμετάσχετε στη συζήτηση μόλις εγκριθεί η αίτησή σας."</string>
|
||||
<string name="screen_join_room_subtitle_no_preview">"Πρέπει να είστε μέλος αυτής της αίθουσας για να δείτε το ιστορικό των μηνυμάτων."</string>
|
||||
<string name="screen_join_room_title_knock">"Θέλετε να συμμετάσχετε σε αυτή την αίθουσα;"</string>
|
||||
<string name="screen_join_room_title_no_preview">"Η προεπισκόπηση δεν είναι διαθέσιμη"</string>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<string name="screen_join_room_loading_alert_title">"Nie udało nam się wyświetlić podglądu tego pokoju"</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"%1$s jeszcze nie obsługuje przestrzeni. Uzyskaj dostęp do przestrzeni w wersji web."</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"Przestrzenie nie są jeszcze obsługiwane"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Kliknij przycisk poniżej, aby powiadomić administratora pokoju. Po zatwierdzeniu będziesz mógł dołączyć do rozmowy."</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Dotknij przycisku poniżej, aby powiadomić administratora czatu. Po zatwierdzeniu będziesz mógł dołączyć do rozmowy."</string>
|
||||
<string name="screen_join_room_subtitle_no_preview">"Musisz być członkiem tego pokoju, aby wyświetlić historię wiadomości."</string>
|
||||
<string name="screen_join_room_title_knock">"Chcesz dołączyć do tego pokoju?"</string>
|
||||
<string name="screen_join_room_title_no_preview">"Podgląd nie jest dostępny"</string>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<string name="screen_join_room_loading_alert_title">"Не удалось показать предпросмотр комнаты"</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"%1$s еще не поддерживает пространства. Вы можете получить к ним доступ в веб-версии."</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"Пространства пока не поддерживаются"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Нажмите кнопку ниже и администратор комнаты получит уведомление. После одобрения Вы сможете присоединиться к беседе."</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Нажмите кнопку ниже и администратор чата получит уведомление. После одобрения Вы сможете присоединиться к беседе."</string>
|
||||
<string name="screen_join_room_subtitle_no_preview">"Вы должны быть участником этой комнаты, чтобы просмотреть историю сообщений."</string>
|
||||
<string name="screen_join_room_title_knock">"Хотите присоединиться к этой комнате?"</string>
|
||||
<string name="screen_join_room_title_no_preview">"Предпросмотр недоступен"</string>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<string name="screen_join_room_loading_alert_title">"Ми не можемо показати попередній перегляд цієї кімнати"</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"%1$s ще не підтримує простори. Ви можете отримати доступ до них у вебверсії."</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"Простори поки що не підтримуються"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Натисніть кнопку нижче, і адміністратор кімнати отримає сповіщення. Ви зможете приєднатися до розмови після схвалення."</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Торкніться кнопки нижче і адміністратор бесіди отримає сповіщення. Ви зможете приєднатися до розмови після схвалення."</string>
|
||||
<string name="screen_join_room_subtitle_no_preview">"Ви мусите бути учасником цієї кімнати, щоб переглядати історію повідомлень."</string>
|
||||
<string name="screen_join_room_title_knock">"Хочете приєднатися до цієї кімнати?"</string>
|
||||
<string name="screen_join_room_title_no_preview">"Попередній перегляд недоступний"</string>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<string name="screen_join_room_loading_alert_title">"无法显示此房间预览"</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"%1$s 暂不支持空间。你可以通过 Web 客户端访问空间。"</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"空间尚未受到支持"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"点击以下按钮以通知房间管理员。获得批准后你将能加入对话。"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"点击以下按钮聊天管理员将收到通知。获得批准后即可加入对话。"</string>
|
||||
<string name="screen_join_room_subtitle_no_preview">"只有房间成员才能查看消息历史。"</string>
|
||||
<string name="screen_join_room_title_knock">"想加入此房间吗?"</string>
|
||||
<string name="screen_join_room_title_no_preview">"预览不可用"</string>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<string name="screen_join_room_loading_alert_title">"We couldn’t display this room preview"</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"%1$s does not support spaces yet. You can access spaces on web."</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"Spaces are not supported yet"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Click the button below and a room administrator will be notified. You’ll be able to join the conversation once approved."</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Tap the button below and a chat administrator will be notified. You\'ll be able to join the conversation once approved."</string>
|
||||
<string name="screen_join_room_subtitle_no_preview">"You must be a member of this room to view the message history."</string>
|
||||
<string name="screen_join_room_title_knock">"Want to join this room?"</string>
|
||||
<string name="screen_join_room_title_no_preview">"Preview is not available"</string>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<string name="screen_knock_requests_list_accept_all_loading_title">"Alle Beitrittsanfragen werden angenommen"</string>
|
||||
<string name="screen_knock_requests_list_accept_failed_alert_description">"Wir konnten diese Beitrittsanfrage nicht annehmen. Möchtest du es noch mal versuchen?"</string>
|
||||
<string name="screen_knock_requests_list_accept_failed_alert_title">"Die Beitrittsanfrage konnte nicht akzeptiert werden"</string>
|
||||
<string name="screen_knock_requests_list_accept_loading_title">"Beitrittsanfrage annehmen"</string>
|
||||
<string name="screen_knock_requests_list_accept_loading_title">"Beitrittsanfrage wird angenommen"</string>
|
||||
<string name="screen_knock_requests_list_ban_alert_confirm_button_title">"Ja, ablehnen und sperren"</string>
|
||||
<string name="screen_knock_requests_list_ban_alert_description">"Bist du sicher, dass du %1$s ablehnen und sperren möchtest? Dieser Nutzer kann dann keinen erneuten Beitritt zu diesem Chat anfragen."</string>
|
||||
<string name="screen_knock_requests_list_ban_alert_title">"Ablehnen und Zugriff sperren"</string>
|
||||
@@ -29,7 +29,7 @@
|
||||
<item quantity="one">"%1$s +%2$d weiterer möchten diesem Chat beitreten"</item>
|
||||
<item quantity="other">"%1$s +%2$d weitere möchten diesem Chat beitreten"</item>
|
||||
</plurals>
|
||||
<string name="screen_room_multiple_knock_requests_view_all_button_title">"Alles ansehen"</string>
|
||||
<string name="screen_room_multiple_knock_requests_view_all_button_title">"Alle anzeigen"</string>
|
||||
<string name="screen_room_single_knock_request_accept_button_title">"Akzeptieren"</string>
|
||||
<string name="screen_room_single_knock_request_title">"%1$s möchte diesem Chat beitreten"</string>
|
||||
<string name="screen_room_single_knock_request_view_button_title">"Ansicht"</string>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="leave_conversation_alert_subtitle">"Bist du sicher, dass du diese Unterhaltung verlassen willst? Diese Unterhaltung ist nicht öffentlich und du kannst ihr ohne Einladung nicht wieder beitreten."</string>
|
||||
<string name="leave_room_alert_empty_subtitle">"Bist du sicher, dass du diesen Chat verlassen möchtest? Du bist die einzige Person hier. Wenn du gehst, kann in Zukunft niemand mehr eintreten, auch du nicht."</string>
|
||||
<string name="leave_room_alert_empty_subtitle">"Bist du sicher, dass du diesen Chat verlassen möchtest? Du bist die einzige Person hier. Wenn du gehst, kann in Zukunft niemand mehr beitreten, auch du nicht."</string>
|
||||
<string name="leave_room_alert_private_subtitle">"Bist du sicher, dass du diesen Chat verlassen möchtest? Dieser Chat ist nicht öffentlich und du kannst ihm ohne Einladung nicht erneut beitreten."</string>
|
||||
<string name="leave_room_alert_select_new_owner_action">"Wähle Eigentümer"</string>
|
||||
<string name="leave_room_alert_select_new_owner_subtitle">"Du bist der einzige Eigentümer dieses Chats. Du musst die Eigentumsrechte an jemand anderen übertragen, bevor du den Chat verlässt."</string>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<string name="screen_link_new_device_desktop_step3">"امسح رمز QR ضوئيًا باستخدام هذا الجهاز"</string>
|
||||
<string name="screen_link_new_device_desktop_submit">"جاهز للمسح الضوئي"</string>
|
||||
<string name="screen_link_new_device_desktop_title">"افتح %1$s على كمبيوتر مكتبي للحصول على رمز QR"</string>
|
||||
<string name="screen_link_new_device_desktop_warning">"لا تقم بمسح رمز الاستجابة السريعة إلا بعد اتباع الخطوات المذكورة أعلاه على جهازك الخاص. لا تقم أبدًا بمسح رمز مرسل من شخص آخر."</string>
|
||||
<string name="screen_link_new_device_enter_number_error_numbers_do_not_match">"الأرقام غير متطابقة"</string>
|
||||
<string name="screen_link_new_device_enter_number_notice">"أدخل الرمز المكوّن من رقمين"</string>
|
||||
<string name="screen_link_new_device_enter_number_subtitle">"سيؤكد هذا أن الاتصال بجهازك الآخر آمن."</string>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<string name="screen_link_new_device_desktop_step3">"Scanne den QR-Code mit diesem Gerät"</string>
|
||||
<string name="screen_link_new_device_desktop_submit">"Bereit zum Scannen"</string>
|
||||
<string name="screen_link_new_device_desktop_title">"Öffne %1$s auf einem Desktop-Computer, um den QR-Code zu erhalten"</string>
|
||||
<string name="screen_link_new_device_desktop_warning">"Scanne einen QR-Code nur, nachdem du die obigen Schritte auf deinem eigenen Gerät ausgeführt hast. Scanne niemals einen Code, den dir eine andere Person geschickt hat."</string>
|
||||
<string name="screen_link_new_device_enter_number_error_numbers_do_not_match">"Die Zahlen stimmen nicht überein"</string>
|
||||
<string name="screen_link_new_device_enter_number_notice">"Gib den 2-stelligen Code ein"</string>
|
||||
<string name="screen_link_new_device_enter_number_subtitle">"Dadurch wird überprüft, ob die Verbindung zu deinem anderen Gerät sicher ist."</string>
|
||||
@@ -19,19 +20,19 @@
|
||||
<string name="screen_link_new_device_error_request_timeout_title">"Die Anmeldung wurde nicht rechtzeitig abgeschlossen"</string>
|
||||
<string name="screen_link_new_device_mobile_step1">"Öffne %1$s auf dem anderen Gerät"</string>
|
||||
<string name="screen_link_new_device_mobile_step2">"Wähle %1$s"</string>
|
||||
<string name="screen_link_new_device_mobile_step2_action">"„Mit QR-Code anmelden”"</string>
|
||||
<string name="screen_link_new_device_mobile_step3">"Scanne den hier gezeigten QR-Code mit dem anderen Gerät."</string>
|
||||
<string name="screen_link_new_device_mobile_step2_action">"„Mit QR-Code anmelden“"</string>
|
||||
<string name="screen_link_new_device_mobile_step3">"Scanne den hier gezeigten QR-Code mit dem anderen Gerät"</string>
|
||||
<string name="screen_link_new_device_mobile_title">"Öffne %1$s auf dem anderen Gerät"</string>
|
||||
<string name="screen_link_new_device_root_desktop_computer">"Desktop-Computer"</string>
|
||||
<string name="screen_link_new_device_root_loading_qr_code">"QR-Code wird geladen…"</string>
|
||||
<string name="screen_link_new_device_root_loading_qr_code">"QR-Code wird geladen …"</string>
|
||||
<string name="screen_link_new_device_root_mobile_device">"Mobilgerät"</string>
|
||||
<string name="screen_link_new_device_root_title">"Welchen Gerätetyp möchtest du verknüpfen?"</string>
|
||||
<string name="screen_link_new_device_wrong_number_subtitle">"Versuch\' es bitte noch mal und stell sicher, dass du den zweistelligen Code richtig eingegeben hast. Wenn die Zahlen immer noch nicht übereinstimmen, wende dich an deinen Kontoanbieter."</string>
|
||||
<string name="screen_link_new_device_wrong_number_subtitle">"Bitte versuche es erneut und stelle sicher, dass du den zweistelligen Code richtig eingegeben hast. Wenn die Zahlen weiterhin nicht übereinstimmen, wende dich an deinen Kontoanbieter."</string>
|
||||
<string name="screen_link_new_device_wrong_number_title">"Die Zahlen stimmen nicht überein"</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_description">"Es konnte keine sichere Verbindung zu dem neuen Gerät hergestellt werden."</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_list_header">"Und jetzt?"</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_list_item_1">"Versuche, dich erneut mit einem QR-Code anzumelden, falls dies ein Netzwerkproblem war."</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"Wenn das Problem bestehen bleibt, versuche es mit einem anderen WLAN-Netzwerk oder verwende deine mobilen Daten statt WLAN."</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_list_item_1">"Versuche, dich erneut mit einem QR-Code anzumelden, falls dies ein Netzwerkproblem war"</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"Wenn das Problem bestehen bleibt, versuche es mit einem anderen WLAN-Netzwerk oder verwende deine mobilen Daten statt WLAN"</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_list_item_3">"Wenn das nicht funktioniert, melde dich manuell an"</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_title">"Die Verbindung ist nicht sicher"</string>
|
||||
<string name="screen_qr_code_login_device_code_subtitle">"Du wirst aufgefordert, die beiden unten abgebildeten Ziffern einzugeben."</string>
|
||||
@@ -41,7 +42,7 @@
|
||||
<string name="screen_qr_code_login_error_declined_subtitle">"Die Anmeldung auf dem anderen Gerät wurde abgelehnt."</string>
|
||||
<string name="screen_qr_code_login_error_declined_title">"Anmelden abgelehnt"</string>
|
||||
<string name="screen_qr_code_login_error_device_already_signed_in_subtitle">"Du musst nichts weiter tun."</string>
|
||||
<string name="screen_qr_code_login_error_device_already_signed_in_title">"Dein anderes Gerät ist schon angemeldet."</string>
|
||||
<string name="screen_qr_code_login_error_device_already_signed_in_title">"Dein anderes Gerät ist schon angemeldet"</string>
|
||||
<string name="screen_qr_code_login_error_expired_subtitle">"Die Anmeldung ist abgelaufen. Bitte versuche es erneut."</string>
|
||||
<string name="screen_qr_code_login_error_expired_title">"Die Anmeldung wurde nicht rechtzeitig abgeschlossen"</string>
|
||||
<string name="screen_qr_code_login_error_linking_not_suported_subtitle">"Dein anderes Gerät unterstützt die Anmeldung bei %s mit einem QR-Code nicht.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<string name="screen_link_new_device_desktop_step3">"Pindai kode QR dengan perangkat ini"</string>
|
||||
<string name="screen_link_new_device_desktop_submit">"Siap untuk memindai"</string>
|
||||
<string name="screen_link_new_device_desktop_title">"Buka %1$s di komputer desktop untuk mendapatkan kode QR."</string>
|
||||
<string name="screen_link_new_device_desktop_warning">"Hanya pindai kode QR setelah mengikuti langkah-langkah di atas di perangkat Anda sendiri. Jangan pernah memindai kode yang dikirim oleh orang lain."</string>
|
||||
<string name="screen_link_new_device_enter_number_error_numbers_do_not_match">"Angkanya tidak cocok"</string>
|
||||
<string name="screen_link_new_device_enter_number_notice">"Masukkan kode 2 digit"</string>
|
||||
<string name="screen_link_new_device_enter_number_subtitle">"Ini akan memverifikasi bahwa koneksi ke perangkat Anda yang lain aman."</string>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<string name="screen_link_new_device_desktop_step3">"Scansiona il codice QR con questo dispositivo"</string>
|
||||
<string name="screen_link_new_device_desktop_submit">"Pronto per la scansione"</string>
|
||||
<string name="screen_link_new_device_desktop_title">"Apri %1$s su un computer desktop per ottenere il codice QR"</string>
|
||||
<string name="screen_link_new_device_desktop_warning">"Scansiona un codice QR solo dopo aver seguito i passaggi sopra descritti sul tuo dispositivo. Non scansionare mai un codice inviato da un\'altra persona."</string>
|
||||
<string name="screen_link_new_device_enter_number_error_numbers_do_not_match">"I numeri non corrispondono"</string>
|
||||
<string name="screen_link_new_device_enter_number_notice">"Inserisci il codice a 2 cifre"</string>
|
||||
<string name="screen_link_new_device_enter_number_subtitle">"Questo verificherà che la connessione con l\'altro dispositivo sia sicura."</string>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<string name="screen_link_new_device_desktop_step3">"この端末でQRコードを読み取る"</string>
|
||||
<string name="screen_link_new_device_desktop_submit">"読み取る"</string>
|
||||
<string name="screen_link_new_device_desktop_title">"%1$s をコンピュータで開き、QRコードを表示してください"</string>
|
||||
<string name="screen_link_new_device_desktop_warning">"あなた自身の端末においてのみ、以上の手順にならってQRコードを読み取ってください。他者から送られたコードは絶対に読み取らないでください。"</string>
|
||||
<string name="screen_link_new_device_enter_number_error_numbers_do_not_match">"数字が一致しません"</string>
|
||||
<string name="screen_link_new_device_enter_number_notice">"2桁の数字を入力してください"</string>
|
||||
<string name="screen_link_new_device_enter_number_subtitle">"他の端末との接続が安全であることを確認します。"</string>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<string name="screen_link_new_device_desktop_step3">"Отсканируйте QR-код с помощью этого устройства"</string>
|
||||
<string name="screen_link_new_device_desktop_submit">"Готово к сканированию"</string>
|
||||
<string name="screen_link_new_device_desktop_title">"Откройте %1$s на компьютере, чтобы получить QR-код"</string>
|
||||
<string name="screen_link_new_device_desktop_warning">"Сканируйте QR-код только после выполнения описанных выше шагов на собственном устройстве. Никогда не сканируйте код, отправленный другим человеком."</string>
|
||||
<string name="screen_link_new_device_enter_number_error_numbers_do_not_match">"Цифры не совпадают"</string>
|
||||
<string name="screen_link_new_device_enter_number_notice">"Введите 2-значный код"</string>
|
||||
<string name="screen_link_new_device_enter_number_subtitle">"Это позволит убедиться в безопасности соединения с другим вашим устройством."</string>
|
||||
|
||||
@@ -29,6 +29,7 @@ dependencies {
|
||||
implementation(projects.features.enterprise.api)
|
||||
implementation(projects.features.messages.api)
|
||||
implementation(libs.maplibre.compose)
|
||||
runtimeOnly(libs.maplibre.compose.runtime.vulkan)
|
||||
implementation(libs.coil)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
|
||||
-10
@@ -9,11 +9,9 @@
|
||||
package io.element.android.features.location.impl.common
|
||||
|
||||
import android.Manifest
|
||||
import androidx.compose.ui.Alignment
|
||||
import org.maplibre.compose.camera.CameraPosition
|
||||
import org.maplibre.compose.map.GestureOptions
|
||||
import org.maplibre.compose.map.MapOptions
|
||||
import org.maplibre.compose.map.OrnamentOptions
|
||||
import org.maplibre.compose.map.RenderOptions
|
||||
import org.maplibre.spatialk.geojson.Position
|
||||
|
||||
@@ -24,14 +22,6 @@ object MapDefaults {
|
||||
val options = MapOptions(
|
||||
renderOptions = RenderOptions.Standard,
|
||||
gestureOptions = GestureOptions.Standard,
|
||||
ornamentOptions = OrnamentOptions(
|
||||
isLogoEnabled = true,
|
||||
logoAlignment = Alignment.BottomStart,
|
||||
isAttributionEnabled = true,
|
||||
attributionAlignment = Alignment.BottomEnd,
|
||||
isCompassEnabled = false,
|
||||
isScaleBarEnabled = false,
|
||||
)
|
||||
)
|
||||
|
||||
val defaultCameraPosition = CameraPosition(
|
||||
|
||||
+21
-7
@@ -7,16 +7,19 @@
|
||||
|
||||
package io.element.android.features.location.impl.common.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.only
|
||||
@@ -32,7 +35,6 @@ import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.rememberBottomSheetScaffoldState
|
||||
import androidx.compose.material3.rememberBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -55,6 +57,9 @@ import org.maplibre.compose.camera.CameraState
|
||||
import org.maplibre.compose.camera.rememberCameraState
|
||||
import org.maplibre.compose.map.MapOptions
|
||||
import org.maplibre.compose.map.MaplibreMap
|
||||
import org.maplibre.compose.overlay.ExpandingAttributionButton
|
||||
import org.maplibre.compose.overlay.MapOverlay
|
||||
import org.maplibre.compose.overlay.MaplibreLogo
|
||||
import org.maplibre.compose.style.BaseStyle
|
||||
import org.maplibre.compose.util.MaplibreComposable
|
||||
import kotlin.math.roundToInt
|
||||
@@ -115,10 +120,6 @@ fun MapBottomSheetScaffold(
|
||||
PaddingValues(bottom = bottomPadding)
|
||||
}
|
||||
}
|
||||
// Update camera position when sheet padding changes
|
||||
LaunchedEffect(sheetPadding) {
|
||||
cameraState.position = cameraState.position.copy(padding = sheetPadding)
|
||||
}
|
||||
BottomSheetScaffold(
|
||||
modifier = Modifier,
|
||||
sheetPeekHeight = sheetPeekHeight,
|
||||
@@ -135,14 +136,27 @@ fun MapBottomSheetScaffold(
|
||||
snackbarHost = snackbarHost,
|
||||
topBar = topBar,
|
||||
) {
|
||||
val ornamentOptions = mapOptions.ornamentOptions.copy(padding = sheetPadding)
|
||||
val mapOptions = mapOptions.copy(ornamentOptions = ornamentOptions)
|
||||
Box {
|
||||
when (customMapTilerConfig) {
|
||||
is AsyncData.Success -> {
|
||||
MaplibreMap(
|
||||
options = mapOptions,
|
||||
cameraPadding = sheetPadding,
|
||||
contentWindowInsets = WindowInsets(bottom = sheetPadding.calculateBottomPadding()),
|
||||
baseStyle = BaseStyle.Uri(rememberTileStyleUrl(customMapTilerConfig.data)),
|
||||
overlay = MapOverlay {
|
||||
val style = styleState
|
||||
Row(
|
||||
Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
MaplibreLogo()
|
||||
ExpandingAttributionButton(cameraState = cameraState, styleState = style)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
cameraState = cameraState,
|
||||
content = mapContent,
|
||||
|
||||
+24
-12
@@ -7,14 +7,16 @@
|
||||
|
||||
package io.element.android.features.location.impl.common.userlocation
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import org.maplibre.compose.location.DesiredAccuracy
|
||||
import org.maplibre.compose.location.rememberNullLocationProvider
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import org.maplibre.compose.location.LocationAccuracy
|
||||
import org.maplibre.compose.location.LocationEvent
|
||||
import org.maplibre.compose.location.LocationRequest
|
||||
import org.maplibre.spatialk.units.extensions.meters
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@@ -22,17 +24,27 @@ import kotlin.time.Duration.Companion.seconds
|
||||
class DefaultUserLocationStateFactory : UserLocationState.Factory {
|
||||
@Composable
|
||||
override fun create(hasLocationPermission: Boolean): UserLocationState {
|
||||
val locationProvider = if (!hasLocationPermission) {
|
||||
rememberNullLocationProvider()
|
||||
val locationProvider = if (hasLocationPermission) {
|
||||
rememberLocationProvider()
|
||||
} else {
|
||||
@SuppressLint("MissingPermission")
|
||||
rememberPlatformLocationProvider(
|
||||
updateInterval = 5.seconds,
|
||||
desiredAccuracy = DesiredAccuracy.High,
|
||||
minDistance = 5.meters,
|
||||
)
|
||||
rememberNotGrantedLocationProvider()
|
||||
}
|
||||
val location by locationProvider.location.collectAsState()
|
||||
val locationFlow = remember(locationProvider) {
|
||||
val locationRequest = LocationRequest(
|
||||
accuracy = LocationAccuracy.High,
|
||||
minimumInterval = 5.seconds,
|
||||
minimumDistance = 5.meters,
|
||||
)
|
||||
locationProvider
|
||||
.updates(locationRequest)
|
||||
.mapNotNull { event ->
|
||||
when (event) {
|
||||
is LocationEvent.Fix -> event.location
|
||||
is LocationEvent.Unavailable -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
val location by locationFlow.collectAsState(null)
|
||||
return UserLocationState(location)
|
||||
}
|
||||
}
|
||||
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.location.impl.common.userlocation
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import android.os.Handler
|
||||
import android.os.HandlerThread
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.location.LocationListenerCompat
|
||||
import androidx.core.location.LocationManagerCompat
|
||||
import androidx.core.location.LocationRequestCompat
|
||||
import androidx.core.os.ExecutorCompat
|
||||
import io.element.android.libraries.core.data.tryOrNull
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import org.maplibre.compose.location.AndroidLocationBackend
|
||||
import org.maplibre.compose.location.LocationAccuracy
|
||||
import org.maplibre.compose.location.LocationEvent
|
||||
import org.maplibre.compose.location.LocationProvider
|
||||
import org.maplibre.compose.location.LocationRequest
|
||||
import org.maplibre.compose.location.LocationUnavailableReason
|
||||
import org.maplibre.compose.location.asMapLibreLocation
|
||||
import org.maplibre.spatialk.units.extensions.inMeters
|
||||
import java.util.ServiceLoader
|
||||
|
||||
/**
|
||||
* Creates the location provider to use.
|
||||
*
|
||||
* Mostly mirrors `createDefaultLocationProvider` from maplibre compose: any [AndroidLocationBackend]
|
||||
* registered through [ServiceLoader] takes precedence, highest [priority]
|
||||
* first. When no backend is available it falls back to [PlatformLocationProvider].
|
||||
*/
|
||||
fun createLocationProvider(context: Context): LocationProvider {
|
||||
val backend = tryOrNull {
|
||||
ServiceLoader.load(AndroidLocationBackend::class.java)
|
||||
.filter { it.isAvailable(context) }
|
||||
.minWithOrNull(compareByDescending<AndroidLocationBackend> { it.priority }.thenBy { it.id })
|
||||
}
|
||||
return backend?.createLocationProvider(context) ?: PlatformLocationProvider(context)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberLocationProvider(context: Context = LocalContext.current): LocationProvider {
|
||||
return remember(context) { createLocationProvider(context) }
|
||||
}
|
||||
|
||||
class PlatformLocationProvider(
|
||||
private val context: Context,
|
||||
) : LocationProvider {
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun updates(request: LocationRequest): Flow<LocationEvent> = callbackFlow {
|
||||
if (!context.hasLocationPermission()) {
|
||||
trySend(LocationEvent.Unavailable(LocationUnavailableReason.PermissionDenied))
|
||||
close()
|
||||
return@callbackFlow
|
||||
}
|
||||
val locationManager = context.getSystemService(LocationManager::class.java)
|
||||
val listener = object : LocationListenerCompat {
|
||||
override fun onLocationChanged(location: Location) {
|
||||
trySend(LocationEvent.Fix(location.asMapLibreLocation()))
|
||||
}
|
||||
|
||||
override fun onProviderDisabled(provider: String) {
|
||||
trySend(LocationEvent.Unavailable(LocationUnavailableReason.ServicesDisabled))
|
||||
}
|
||||
}
|
||||
|
||||
var registered = false
|
||||
|
||||
fun refreshRegistration() {
|
||||
if (registered) {
|
||||
LocationManagerCompat.removeUpdates(locationManager, listener)
|
||||
registered = false
|
||||
}
|
||||
if (!LocationManagerCompat.isLocationEnabled(locationManager)) {
|
||||
trySend(LocationEvent.Unavailable(LocationUnavailableReason.ServicesDisabled))
|
||||
return
|
||||
}
|
||||
val provider = PROVIDERS_BY_PRIORITY.firstOrNull { LocationManagerCompat.hasProvider(locationManager, it) }
|
||||
if (provider == null) {
|
||||
trySend(LocationEvent.Unavailable(LocationUnavailableReason.Unsupported))
|
||||
return
|
||||
}
|
||||
locationManager.getLastKnownLocation(provider)?.let { location ->
|
||||
trySend(LocationEvent.Fix(location.asMapLibreLocation()))
|
||||
}
|
||||
val locationRequestCompat = LocationRequestCompat.Builder(request.minimumInterval.inWholeMilliseconds)
|
||||
.setQuality(request.accuracy.toLocationRequestQuality())
|
||||
.setMinUpdateDistanceMeters(request.minimumDistance.inMeters.toFloat())
|
||||
.build()
|
||||
LocationManagerCompat.requestLocationUpdates(
|
||||
locationManager,
|
||||
provider,
|
||||
locationRequestCompat,
|
||||
ExecutorCompat.create(Handler(handlerThread.looper)),
|
||||
listener,
|
||||
)
|
||||
registered = true
|
||||
}
|
||||
|
||||
// Re-select and re-register when the user toggles location settings, so updates resume
|
||||
// automatically once a usable provider comes back.
|
||||
val settingsReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
try {
|
||||
refreshRegistration()
|
||||
} catch (error: SecurityException) {
|
||||
trySend(LocationEvent.Unavailable(LocationUnavailableReason.PermissionDenied, error))
|
||||
close()
|
||||
} catch (error: IllegalArgumentException) {
|
||||
trySend(LocationEvent.Unavailable(LocationUnavailableReason.UnexpectedFailure, error))
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
ContextCompat.registerReceiver(
|
||||
context,
|
||||
settingsReceiver,
|
||||
IntentFilter().apply {
|
||||
addAction(LocationManager.MODE_CHANGED_ACTION)
|
||||
addAction(LocationManager.PROVIDERS_CHANGED_ACTION)
|
||||
},
|
||||
ContextCompat.RECEIVER_NOT_EXPORTED,
|
||||
)
|
||||
refreshRegistration()
|
||||
} catch (error: SecurityException) {
|
||||
trySend(LocationEvent.Unavailable(LocationUnavailableReason.PermissionDenied, error))
|
||||
close()
|
||||
} catch (error: IllegalArgumentException) {
|
||||
trySend(LocationEvent.Unavailable(LocationUnavailableReason.UnexpectedFailure, error))
|
||||
close()
|
||||
}
|
||||
awaitClose {
|
||||
tryOrNull { context.unregisterReceiver(settingsReceiver) }
|
||||
LocationManagerCompat.removeUpdates(locationManager, listener)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private val PROVIDERS_BY_PRIORITY = listOf(
|
||||
LocationManager.FUSED_PROVIDER,
|
||||
LocationManager.GPS_PROVIDER,
|
||||
LocationManager.NETWORK_PROVIDER,
|
||||
)
|
||||
private val handlerThread by lazy {
|
||||
HandlerThread("PlatformLocationProvider").apply { start() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Context.hasLocationPermission(): Boolean =
|
||||
checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) ==
|
||||
PackageManager.PERMISSION_GRANTED ||
|
||||
checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) ==
|
||||
PackageManager.PERMISSION_GRANTED
|
||||
|
||||
private fun LocationAccuracy.toLocationRequestQuality(): Int = when (this) {
|
||||
LocationAccuracy.BestForNavigation, LocationAccuracy.High -> LocationRequestCompat.QUALITY_HIGH_ACCURACY
|
||||
LocationAccuracy.Balanced -> LocationRequestCompat.QUALITY_BALANCED_POWER_ACCURACY
|
||||
LocationAccuracy.Low, LocationAccuracy.Lowest -> LocationRequestCompat.QUALITY_LOW_POWER
|
||||
}
|
||||
|
||||
class NotGrantedLocationProvider : LocationProvider {
|
||||
override fun updates(request: LocationRequest): Flow<LocationEvent> {
|
||||
return flowOf(LocationEvent.Unavailable(LocationUnavailableReason.PermissionDenied))
|
||||
}
|
||||
}
|
||||
@Composable
|
||||
fun rememberNotGrantedLocationProvider(): NotGrantedLocationProvider {
|
||||
return remember { NotGrantedLocationProvider() }
|
||||
}
|
||||
-129
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.location.impl.common.userlocation
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.LocationManager
|
||||
import android.os.Handler
|
||||
import android.os.HandlerThread
|
||||
import androidx.annotation.RequiresPermission
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.location.LocationListenerCompat
|
||||
import androidx.core.location.LocationManagerCompat
|
||||
import androidx.core.location.LocationRequestCompat
|
||||
import androidx.core.os.ExecutorCompat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import org.maplibre.compose.location.DesiredAccuracy
|
||||
import org.maplibre.compose.location.Location
|
||||
import org.maplibre.compose.location.LocationProvider
|
||||
import org.maplibre.compose.location.PermissionException
|
||||
import org.maplibre.compose.location.asMapLibreLocation
|
||||
import org.maplibre.spatialk.units.Length
|
||||
import org.maplibre.spatialk.units.extensions.inMeters
|
||||
import kotlin.time.Duration
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
class PlatformLocationProvider(
|
||||
context: Context,
|
||||
private val updateInterval: Duration,
|
||||
private val minDistance: Length,
|
||||
private val desiredAccuracy: DesiredAccuracy = DesiredAccuracy.High,
|
||||
coroutineScope: CoroutineScope,
|
||||
sharingStarted: SharingStarted = SharingStarted.WhileSubscribed(stopTimeoutMillis = 1000),
|
||||
) : LocationProvider {
|
||||
override val location: StateFlow<Location?>
|
||||
|
||||
init {
|
||||
if (!handlerThread.isAlive) handlerThread.start()
|
||||
if (
|
||||
context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
|
||||
context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
throw PermissionException()
|
||||
}
|
||||
val locationManager = context.getSystemService(LocationManager::class.java)
|
||||
val provider = PROVIDERS_BY_PRIORITY.firstOrNull { LocationManagerCompat.hasProvider(locationManager, it) }
|
||||
val locationFlow = if (provider != null) {
|
||||
createProviderFlow(locationManager, provider)
|
||||
} else {
|
||||
emptyFlow()
|
||||
}
|
||||
location = locationFlow.stateIn(coroutineScope, sharingStarted, null)
|
||||
}
|
||||
|
||||
@RequiresPermission(
|
||||
anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION]
|
||||
)
|
||||
private fun createProviderFlow(locationManager: LocationManager, provider: String) = callbackFlow {
|
||||
send(locationManager.getLastKnownLocation(provider)?.asMapLibreLocation())
|
||||
val listener = LocationListenerCompat { trySend(it.asMapLibreLocation()) }
|
||||
val request = LocationRequestCompat.Builder(updateInterval.inWholeMilliseconds)
|
||||
.setQuality(desiredAccuracy.toLocationRequestQuality())
|
||||
.setMinUpdateDistanceMeters(minDistance.inMeters.toFloat())
|
||||
.build()
|
||||
LocationManagerCompat.requestLocationUpdates(
|
||||
locationManager,
|
||||
provider,
|
||||
request,
|
||||
ExecutorCompat.create(Handler(handlerThread.looper)),
|
||||
listener,
|
||||
)
|
||||
awaitClose { LocationManagerCompat.removeUpdates(locationManager, listener) }
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private val PROVIDERS_BY_PRIORITY = listOf(
|
||||
LocationManager.FUSED_PROVIDER,
|
||||
LocationManager.GPS_PROVIDER,
|
||||
LocationManager.NETWORK_PROVIDER,
|
||||
)
|
||||
private val handlerThread by lazy { HandlerThread("PlatformLocationProvider") }
|
||||
}
|
||||
}
|
||||
|
||||
private fun DesiredAccuracy.toLocationRequestQuality(): Int = when (this) {
|
||||
DesiredAccuracy.Highest, DesiredAccuracy.High -> LocationRequestCompat.QUALITY_HIGH_ACCURACY
|
||||
DesiredAccuracy.Balanced -> LocationRequestCompat.QUALITY_BALANCED_POWER_ACCURACY
|
||||
DesiredAccuracy.Low, DesiredAccuracy.Lowest -> LocationRequestCompat.QUALITY_LOW_POWER
|
||||
}
|
||||
|
||||
@Composable
|
||||
@RequiresPermission(
|
||||
anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION]
|
||||
)
|
||||
fun rememberPlatformLocationProvider(
|
||||
updateInterval: Duration,
|
||||
minDistance: Length,
|
||||
desiredAccuracy: DesiredAccuracy = DesiredAccuracy.High,
|
||||
context: Context = LocalContext.current,
|
||||
coroutineScope: CoroutineScope = rememberCoroutineScope(),
|
||||
sharingStarted: SharingStarted = SharingStarted.WhileSubscribed(stopTimeoutMillis = 1000),
|
||||
): PlatformLocationProvider {
|
||||
return remember(context, updateInterval, minDistance, desiredAccuracy, coroutineScope, sharingStarted) {
|
||||
PlatformLocationProvider(
|
||||
context = context,
|
||||
updateInterval = updateInterval,
|
||||
minDistance = minDistance,
|
||||
desiredAccuracy = desiredAccuracy,
|
||||
coroutineScope = coroutineScope,
|
||||
sharingStarted = sharingStarted,
|
||||
)
|
||||
}
|
||||
}
|
||||
+2
@@ -8,8 +8,10 @@
|
||||
package io.element.android.features.location.impl.common.userlocation
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import org.maplibre.compose.location.Location
|
||||
|
||||
@Immutable
|
||||
data class UserLocationState(val location: Location?) {
|
||||
fun interface Factory {
|
||||
@Composable
|
||||
|
||||
+33
-27
@@ -14,7 +14,7 @@ import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION
|
||||
import android.os.IBinder
|
||||
import androidx.core.app.ServiceCompat
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.features.location.impl.common.userlocation.PlatformLocationProvider
|
||||
import io.element.android.features.location.impl.common.userlocation.createLocationProvider
|
||||
import io.element.android.features.location.impl.di.LocationBindings
|
||||
import io.element.android.features.location.impl.live.notification.LiveLocationSharingNotificationCreator
|
||||
import io.element.android.libraries.architecture.bindings
|
||||
@@ -30,15 +30,14 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import org.maplibre.compose.location.DesiredAccuracy
|
||||
import org.maplibre.compose.location.PermissionException
|
||||
import org.maplibre.compose.location.LocationAccuracy
|
||||
import org.maplibre.compose.location.LocationEvent
|
||||
import org.maplibre.compose.location.LocationRequest
|
||||
import org.maplibre.compose.location.LocationUnavailableReason
|
||||
import org.maplibre.spatialk.units.extensions.inMeters
|
||||
import org.maplibre.spatialk.units.extensions.meters
|
||||
import timber.log.Timber
|
||||
@@ -91,31 +90,38 @@ class LiveLocationSharingService : Service() {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private fun startLocationUpdatesListener() {
|
||||
Timber.d("LiveLocationSharingService listening to location updates")
|
||||
val locationProvider = createLocationProvider(context = applicationContext)
|
||||
appPreferencesStore.getLiveLocationMinimumDistanceInMetersUpdateFlow()
|
||||
.flatMapLatest { minDistanceMeters ->
|
||||
try {
|
||||
PlatformLocationProvider(
|
||||
context = applicationContext,
|
||||
updateInterval = UPDATE_INTERVAL_IN_SECOND.seconds,
|
||||
minDistance = minDistanceMeters.meters,
|
||||
desiredAccuracy = DesiredAccuracy.Balanced,
|
||||
coroutineScope = coroutineScope
|
||||
).location
|
||||
} catch (exception: PermissionException) {
|
||||
Timber.e(exception, "Failed to create PlatformLocationProvider")
|
||||
coordinator.dispatchUnrecoverableError()
|
||||
emptyFlow()
|
||||
val locationRequest = LocationRequest(
|
||||
accuracy = LocationAccuracy.Balanced,
|
||||
minimumInterval = UPDATE_INTERVAL_IN_SECOND.seconds,
|
||||
minimumDistance = minDistanceMeters.meters
|
||||
)
|
||||
locationProvider.updates(locationRequest)
|
||||
}
|
||||
.onEach { locationEvent ->
|
||||
when (locationEvent) {
|
||||
is LocationEvent.Fix -> {
|
||||
val location = ApiLocation(
|
||||
lat = locationEvent.location.position.value.latitude,
|
||||
lon = locationEvent.location.position.value.longitude,
|
||||
accuracy = locationEvent.location.position.accuracy?.inMeters?.toFloat(),
|
||||
)
|
||||
coordinator.dispatch(location)
|
||||
}
|
||||
is LocationEvent.Unavailable -> {
|
||||
when (locationEvent.reason) {
|
||||
LocationUnavailableReason.ServicesDisabled,
|
||||
LocationUnavailableReason.TemporarilyUnavailable -> Unit
|
||||
LocationUnavailableReason.Unsupported,
|
||||
LocationUnavailableReason.Misconfigured,
|
||||
LocationUnavailableReason.PermissionDenied,
|
||||
LocationUnavailableReason.UnexpectedFailure -> coordinator.dispatchUnrecoverableError()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.filterNotNull()
|
||||
.map { location ->
|
||||
ApiLocation(
|
||||
lat = location.position.value.latitude,
|
||||
lon = location.position.value.longitude,
|
||||
accuracy = location.position.accuracy?.inMeters?.toFloat(),
|
||||
)
|
||||
}
|
||||
.onEach(coordinator::dispatch)
|
||||
.launchIn(coroutineScope)
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -107,6 +107,7 @@ fun ShareLocationView(
|
||||
val scaffoldState = rememberBottomSheetScaffoldState(
|
||||
bottomSheetState = rememberBottomSheetState(
|
||||
initialValue = SheetValue.Expanded,
|
||||
enabledValues = setOf(SheetValue.PartiallyExpanded, SheetValue.Expanded),
|
||||
)
|
||||
)
|
||||
val cameraState = rememberCameraState(firstPosition = MapDefaults.defaultCameraPosition)
|
||||
|
||||
+1
-1
@@ -94,6 +94,7 @@ fun ShowLocationView(
|
||||
val scaffoldState = rememberBottomSheetScaffoldState(
|
||||
bottomSheetState = rememberBottomSheetState(
|
||||
initialValue = SheetValue.Expanded,
|
||||
enabledValues = setOf(SheetValue.PartiallyExpanded, SheetValue.Expanded),
|
||||
)
|
||||
)
|
||||
LaunchedEffect(state.isSheetDraggable) {
|
||||
@@ -154,7 +155,6 @@ fun ShowLocationView(
|
||||
modifier = Modifier.clickable {
|
||||
state.eventSink(ShowLocationEvent.TrackMyLocation(false))
|
||||
val position = CameraPosition(
|
||||
padding = sheetPaddings,
|
||||
target = Position(locationShare.location.lon, locationShare.location.lat),
|
||||
// Force pointing to NORTH
|
||||
bearing = 0.0,
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_share_location_live_location_disclaimer_title">"Der Verlauf deines Live-Standorts wird im Chat gespeichert und bleibt für die Mitglieder sichtbar, auch nachdem du die Freigabe beendet hast."</string>
|
||||
<string name="screen_share_location_live_location_duration_picker_title">"Wie lange soll der Live-Standort geteilt werden?"</string>
|
||||
<string name="screen_share_location_live_location_missing_permissions">"Du hast keine Berechtigung, deinen Live-Standort in diesem Chat zu teilen"</string>
|
||||
</resources>
|
||||
|
||||
@@ -7,20 +7,20 @@
|
||||
<string name="screen_app_lock_forgot_pin">"PIN vergessen?"</string>
|
||||
<string name="screen_app_lock_settings_change_pin">"PIN-Code ändern"</string>
|
||||
<string name="screen_app_lock_settings_enable_biometric_unlock">"Biometrisches Entsperren zulassen"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin">"Pin entfernen"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin">"PIN entfernen"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin_alert_message">"Bist du sicher, dass du die PIN entfernen willst?"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin_alert_title">"PIN entfernen?"</string>
|
||||
<string name="screen_app_lock_setup_biometric_unlock_allow_title">"%1$s zulassen"</string>
|
||||
<string name="screen_app_lock_setup_biometric_unlock_skip">"Ich möchte diese PIN verwenden."</string>
|
||||
<string name="screen_app_lock_setup_biometric_unlock_skip">"Ich würde lieber eine PIN verwenden"</string>
|
||||
<string name="screen_app_lock_setup_biometric_unlock_subtitle">"Spare dir etwas Zeit und benutze %1$s, um die App zu entsperren"</string>
|
||||
<string name="screen_app_lock_setup_choose_pin">"PIN wählen"</string>
|
||||
<string name="screen_app_lock_setup_confirm_pin">"PIN bestätigen"</string>
|
||||
<string name="screen_app_lock_setup_pin_context">"Sperre %1$s um deine Chats zusätzlich abzusichern.
|
||||
|
||||
Wähle eine einprägsame PIN. Wenn du sie vergisst, wirst du aus der App abgemeldet."</string>
|
||||
<string name="screen_app_lock_setup_pin_forbidden_dialog_content">"Aus Sicherheitsgründen kann dieser PIN-Code nicht verwendet werden."</string>
|
||||
<string name="screen_app_lock_setup_pin_forbidden_dialog_title">"Bitte eine andere PIN verwenden."</string>
|
||||
<string name="screen_app_lock_setup_pin_mismatch_dialog_content">"Bitte gib die gleiche PIN wie zuvor ein."</string>
|
||||
<string name="screen_app_lock_setup_pin_forbidden_dialog_content">"Aus Sicherheitsgründen kann dieser PIN-Code nicht verwendet werden"</string>
|
||||
<string name="screen_app_lock_setup_pin_forbidden_dialog_title">"Bitte eine andere PIN verwenden"</string>
|
||||
<string name="screen_app_lock_setup_pin_mismatch_dialog_content">"Bitte gib die gleiche PIN wie zuvor ein"</string>
|
||||
<string name="screen_app_lock_setup_pin_mismatch_dialog_title">"Die PINs stimmen nicht überein"</string>
|
||||
<string name="screen_app_lock_signout_alert_message">"Um fortzufahren, musst du dich erneut anmelden und eine neue PIN erstellen"</string>
|
||||
<string name="screen_app_lock_signout_alert_title">"Dieses Gerät wurde entfernt"</string>
|
||||
|
||||
@@ -25,8 +25,16 @@
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_title">"موفّر الحساب %1$s غير مسموح به."</string>
|
||||
<string name="screen_change_server_form_header">"عنوان الـ URL للسيرفر"</string>
|
||||
<string name="screen_change_server_form_notice">"أدخل عنوان نطاق."</string>
|
||||
<string name="screen_change_server_navigation_title_login">"تسجيل الدخول"</string>
|
||||
<string name="screen_change_server_navigation_title_register">"إنشاء حساب"</string>
|
||||
<string name="screen_change_server_subtitle">"ما عنوان السيرفر خاصتك؟"</string>
|
||||
<string name="screen_change_server_textfield_footer_login">"إذا كان حسابك مقدمًا من شركتك أو مجتمع ما، فقم بتغيير مزود الحساب (مثال: companyname.com)."</string>
|
||||
<string name="screen_change_server_textfield_footer_register">"أنشئ حسابًا على موقع matrix.org مجانًا. إذا كنت ترغب في إنشاء حساب مع مزود خدمة مختلف، فقم بتغييره وفقًا لذلك (مثال: communityname.org)."</string>
|
||||
<string name="screen_change_server_textfield_header">"مزود الحساب"</string>
|
||||
<string name="screen_change_server_textfield_placeholder">"example.com"</string>
|
||||
<string name="screen_change_server_title">"اختر خادمك"</string>
|
||||
<string name="screen_change_server_title_login">"أدخل مزود حسابك"</string>
|
||||
<string name="screen_change_server_title_register">"اختر مزود خدمة الحساب"</string>
|
||||
<string name="screen_create_account_title">"إنشاء حساب"</string>
|
||||
<string name="screen_login_error_deactivated_account">"هذا الحساب محذوف."</string>
|
||||
<string name="screen_login_error_invalid_credentials">"اسم المستخدم و/أو كلمة المرور غير صحيحة"</string>
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
<string name="screen_account_provider_form_notice">"Gib einen Suchbegriff oder eine Domainadresse ein."</string>
|
||||
<string name="screen_account_provider_form_subtitle">"Suche nach einem Unternehmen, einer Community oder einem privaten Server."</string>
|
||||
<string name="screen_account_provider_form_title">"Kontoanbieter finden"</string>
|
||||
<string name="screen_account_provider_signin_subtitle">"Hier werden deine Gespräche gespeichert - so wie du deine E-Mails bei einem E-Mail-Anbieter aufbewahren würdest."</string>
|
||||
<string name="screen_account_provider_signin_subtitle">"Hier werden deine Gespräche gespeichert – so wie du deine E-Mails bei einem E-Mail-Anbieter aufbewahren würdest."</string>
|
||||
<string name="screen_account_provider_signin_title">"Du bist dabei, dich bei %s anzumelden"</string>
|
||||
<string name="screen_account_provider_signup_subtitle">"Hier werden deine Gespräche gespeichert - so wie du deine E-Mails bei einem E-Mail-Anbieter aufbewahren würdest."</string>
|
||||
<string name="screen_account_provider_signup_subtitle">"Hier werden deine Gespräche gespeichert – so wie du deine E-Mails bei einem E-Mail-Anbieter aufbewahren würdest."</string>
|
||||
<string name="screen_account_provider_signup_title">"Du bist dabei, ein Konto bei %s zu erstellen"</string>
|
||||
<string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org ist ein großer, kostenloser Server im öffentlichen Matrix-Netzwerk für eine sichere, dezentralisierte Kommunikation, der von der Matrix.org Foundation betrieben wird."</string>
|
||||
<string name="screen_change_account_provider_other">"Sonstige"</string>
|
||||
@@ -16,23 +16,31 @@
|
||||
<string name="screen_change_server_error_element_pro_required_action_android">"Google Play"</string>
|
||||
<string name="screen_change_server_error_element_pro_required_message">"Auf %1$s ist die Element Pro App erforderlich. Bitte lade diese aus dem Store."</string>
|
||||
<string name="screen_change_server_error_element_pro_required_title">"Element Pro erforderlich"</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"Wir konnten diesen Homeserver nicht erreichen. Bitte überprüfe, ob du die Homeserver-URL korrekt eingegeben hast. Wenn die URL korrekt ist, wende dich an deinen Homeserver-Administrator, um weitere Hilfe zu erhalten."</string>
|
||||
<string name="screen_change_server_error_invalid_well_known">"Der Server ist aufgrund eines Problems in der \".well-known\" Datei nicht verfügbar:
|
||||
<string name="screen_change_server_error_invalid_homeserver">"Wir konnten diesen Kontoanbieter nicht erreichen. Bitte überprüfe, ob du die Homeserver-URL korrekt eingegeben hast. Wenn die URL korrekt ist, wende dich an deinen Kontoanbieter, um weitere Hilfe zu erhalten."</string>
|
||||
<string name="screen_change_server_error_invalid_well_known">"Der Server ist aufgrund eines Problems in der Datei .well-known nicht verfügbar:
|
||||
%1$s"</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"Der gewählte Kontoanbieter unterstützt Sliding-Sync nicht. Für die Verwendung von %1$s ist eine Aktualisierung des Servers erforderlich."</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"Der gewählte Kontoanbieter unterstützt Sliding-Sync nicht. Für die Verwendung von %1$s ist eine Aktualisierung erforderlich."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver">"%1$s darf keine Verbindung zu %2$s herstellen."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_content">"Die App wurde so konfiguriert, dass sie %1$s zulässt."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_title">"Kontoanbieter %1$s ist nicht zulässig."</string>
|
||||
<string name="screen_change_server_form_header">"Homeserver-URL"</string>
|
||||
<string name="screen_change_server_form_notice">"Gib eine Domain-Adresse ein."</string>
|
||||
<string name="screen_change_server_navigation_title_login">"Anmelden"</string>
|
||||
<string name="screen_change_server_navigation_title_register">"Konto erstellen"</string>
|
||||
<string name="screen_change_server_subtitle">"Wie lautet die Adresse deines Servers?"</string>
|
||||
<string name="screen_change_server_textfield_footer_login">"Wenn dein Konto von deinem Unternehmen oder einer Community bereitgestellt wird, ändere den Kontoanbieter (Beispiel: firmenname.com)."</string>
|
||||
<string name="screen_change_server_textfield_footer_register">"Erstelle kostenlos ein Konto auf matrix.org. Wenn du ein Konto bei einem anderen Anbieter erstellen möchtest, passe die Eingabe entsprechend an (Beispiel: communityname.org)."</string>
|
||||
<string name="screen_change_server_textfield_header">"Kontoanbieter"</string>
|
||||
<string name="screen_change_server_textfield_placeholder">"example.com"</string>
|
||||
<string name="screen_change_server_title">"Wähle deinen Server aus"</string>
|
||||
<string name="screen_change_server_title_login">"Gib deinen Kontoanbieter ein"</string>
|
||||
<string name="screen_change_server_title_register">"Wähle einen Kontoanbieter"</string>
|
||||
<string name="screen_create_account_title">"Konto erstellen"</string>
|
||||
<string name="screen_login_error_deactivated_account">"Dieses Konto wurde gelöscht."</string>
|
||||
<string name="screen_login_error_invalid_credentials">"Falscher Nutzername und/oder Passwort"</string>
|
||||
<string name="screen_login_error_invalid_user_id">"Dies ist keine gültige Nutzerkennung. Erwartetes Format: \'@nutzer:homeserver.org\'"</string>
|
||||
<string name="screen_login_error_invalid_user_id">"Dies ist keine gültige Nutzerkennung. Erwartetes Format: ‚@nutzer:homeserver.org‘"</string>
|
||||
<string name="screen_login_error_refresh_tokens">"Dieser Server ist so konfiguriert, dass er Refresh-Tokens verwendet. Diese werden für die passwortbasierte Anmeldung nicht unterstützt."</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"Der ausgewählte Homeserver unterstützt weder den Login per Passwort noch per OAuth. Bitte kontaktiere deinen Administrator oder wähle einen anderen Homeserver."</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"Der ausgewählte Kontoanbieter unterstützt weder die Anmeldung per Passwort noch per OAuth. Wende dich an deinen Kontoanbieter oder wähle einen anderen."</string>
|
||||
<string name="screen_login_form_header">"Gib deine Daten ein"</string>
|
||||
<string name="screen_login_subtitle">"Matrix ist ein offenes Netzwerk für eine sichere, dezentrale Kommunikation."</string>
|
||||
<string name="screen_login_title">"Willkommen zurück!"</string>
|
||||
@@ -40,9 +48,10 @@
|
||||
<string name="screen_missing_key_backup_open_element_classic">"Element Classic öffnen"</string>
|
||||
<string name="screen_missing_key_backup_step_1">"Öffne Element Classic auf deinem Gerät"</string>
|
||||
<string name="screen_missing_key_backup_step_2_android">"Geh zu Einstellungen > Sicherheit & Datenschutz"</string>
|
||||
<string name="screen_missing_key_backup_step_4">"Folge den Anweisungen, um den Schlüsselspeicher zu aktivieren."</string>
|
||||
<string name="screen_missing_key_backup_step_3_android">"Wähle unter „Verwaltung der Verschlüsselungs-Schlüssel“ den Eintrag „Wiederherstellung verschlüsselter Nachrichten“"</string>
|
||||
<string name="screen_missing_key_backup_step_4">"Folge den Anweisungen, um deinen Schlüsselspeicher zu aktivieren"</string>
|
||||
<string name="screen_missing_key_backup_step_5">"Zurück zu %1$s"</string>
|
||||
<string name="screen_missing_key_backup_title">"Aktiviere deinen Schlüsselspeicher, bevor du mit %1$s fortfährst."</string>
|
||||
<string name="screen_missing_key_backup_title">"Aktiviere deinen Schlüsselspeicher, bevor du mit %1$s fortfährst"</string>
|
||||
<string name="screen_onboarding_app_version">"Version %1$s"</string>
|
||||
<string name="screen_onboarding_checking_account">"Konto wird geprüft"</string>
|
||||
<string name="screen_onboarding_sign_in_manually">"Manuell anmelden"</string>
|
||||
@@ -52,12 +61,12 @@
|
||||
<string name="screen_onboarding_welcome_back">"Willkommen zurück"</string>
|
||||
<string name="screen_onboarding_welcome_message">"Willkommen beim schnellsten %1$s aller Zeiten. Optimiert für Geschwindigkeit und Einfachheit."</string>
|
||||
<string name="screen_onboarding_welcome_subtitle">"Willkommen zu %1$s. Aufgeladen, für Geschwindigkeit und Einfachheit."</string>
|
||||
<string name="screen_onboarding_welcome_title">"Sei in Deinem Element"</string>
|
||||
<string name="screen_qr_code_login_connecting_subtitle">"Sichere Verbindung aufbauen"</string>
|
||||
<string name="screen_onboarding_welcome_title">"Sei in deinem Element"</string>
|
||||
<string name="screen_qr_code_login_connecting_subtitle">"Sichere Verbindung wird aufgebaut"</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_description">"Es konnte keine sichere Verbindung zu dem neuen Gerät hergestellt werden."</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_list_header">"Und jetzt?"</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_list_item_1">"Versuche, dich erneut mit einem QR-Code anzumelden, falls dies ein Netzwerkproblem war."</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"Wenn das Problem bestehen bleibt, versuche es mit einem anderen WLAN-Netzwerk oder verwende deine mobilen Daten statt WLAN."</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_list_item_1">"Versuche, dich erneut mit einem QR-Code anzumelden, falls dies ein Netzwerkproblem war"</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"Wenn das Problem bestehen bleibt, versuche es mit einem anderen WLAN-Netzwerk oder verwende deine mobilen Daten statt WLAN"</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_list_item_3">"Wenn das nicht funktioniert, melde dich manuell an"</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_title">"Die Verbindung ist nicht sicher"</string>
|
||||
<string name="screen_qr_code_login_device_code_subtitle">"Du wirst aufgefordert, die beiden unten abgebildeten Ziffern einzugeben."</string>
|
||||
@@ -69,7 +78,7 @@
|
||||
<string name="screen_qr_code_login_error_declined_subtitle">"Die Anmeldung auf dem anderen Gerät wurde abgelehnt."</string>
|
||||
<string name="screen_qr_code_login_error_declined_title">"Anmelden abgelehnt"</string>
|
||||
<string name="screen_qr_code_login_error_device_already_signed_in_subtitle">"Du musst nichts weiter tun."</string>
|
||||
<string name="screen_qr_code_login_error_device_already_signed_in_title">"Dein anderes Gerät ist schon angemeldet."</string>
|
||||
<string name="screen_qr_code_login_error_device_already_signed_in_title">"Dein anderes Gerät ist schon angemeldet"</string>
|
||||
<string name="screen_qr_code_login_error_expired_subtitle">"Die Anmeldung ist abgelaufen. Bitte versuche es erneut."</string>
|
||||
<string name="screen_qr_code_login_error_expired_title">"Die Anmeldung wurde nicht rechtzeitig abgeschlossen"</string>
|
||||
<string name="screen_qr_code_login_error_linking_not_suported_subtitle">"Dein anderes Gerät unterstützt die Anmeldung bei %s mit einem QR-Code nicht.
|
||||
@@ -80,9 +89,9 @@ Versuche, dich manuell anzumelden, oder scanne den QR-Code mit einem anderen Ger
|
||||
<string name="screen_qr_code_login_error_sliding_sync_not_supported_title">"%1$s wird nicht unterstützt"</string>
|
||||
<string name="screen_qr_code_login_initial_state_button_title">"Bereit zum Scannen"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_1">"%1$s auf einem Desktop-Gerät öffnen"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_2">"Klick auf deinen Avatar"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_2">"Wähle dein Profilbild"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_3">"Wähle %1$s"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_3_action">"\"Neues Gerät verknüpfen\""</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_3_action">"„Neues Gerät verknüpfen“"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_4">"Scanne den QR-Code mit diesem Gerät"</string>
|
||||
<string name="screen_qr_code_login_initial_state_subtitle">"Nur verfügbar falls dein Kontoanbieter dies unterstützt."</string>
|
||||
<string name="screen_qr_code_login_initial_state_title">"Öffne %1$s auf einem anderen Gerät, um den QR-Code zu erhalten"</string>
|
||||
@@ -96,12 +105,12 @@ Versuche, dich manuell anzumelden, oder scanne den QR-Code mit einem anderen Ger
|
||||
<string name="screen_qr_code_login_start_over_button">"Neu beginnen"</string>
|
||||
<string name="screen_qr_code_login_unknown_error_description">"Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es erneut."</string>
|
||||
<string name="screen_qr_code_login_verify_code_loading">"Warten auf dein anderes Gerät"</string>
|
||||
<string name="screen_qr_code_login_verify_code_subtitle">"Dein Konto-Provider kann nach dem folgenden Code fragen, um die Anmeldung zu bestätigen."</string>
|
||||
<string name="screen_qr_code_login_verify_code_subtitle">"Dein Kontoanbieter fragt möglicherweise nach dem folgenden Code, um die Anmeldung zu bestätigen."</string>
|
||||
<string name="screen_qr_code_login_verify_code_title">"Dein Verifizierungscode"</string>
|
||||
<string name="screen_server_confirmation_change_server">"Kontoanbieter wechseln"</string>
|
||||
<string name="screen_server_confirmation_message_login_element_dot_io">"Ein privater Server für die Mitarbeiter von Element."</string>
|
||||
<string name="screen_server_confirmation_message_login_matrix_dot_org">"Matrix ist ein offenes Netzwerk für eine sichere, dezentrale Kommunikation."</string>
|
||||
<string name="screen_server_confirmation_message_register">"Hier werden deine Gespräche gespeichert - so wie du deine E-Mails bei einem E-Mail-Anbieter aufbewahren würdest."</string>
|
||||
<string name="screen_server_confirmation_message_register">"Hier werden deine Gespräche gespeichert – so wie du deine E-Mails bei einem E-Mail-Anbieter aufbewahren würdest."</string>
|
||||
<string name="screen_server_confirmation_title_login">"Du bist dabei, dich bei %1$s anzumelden"</string>
|
||||
<string name="screen_server_confirmation_title_picker_mode">"Kontoanbieter auswählen"</string>
|
||||
<string name="screen_server_confirmation_title_register">"Du bist dabei, auf %1$s ein Konto zu erstellen"</string>
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
<string name="screen_change_server_error_element_pro_required_action_android">"Google Play"</string>
|
||||
<string name="screen_change_server_error_element_pro_required_message">"Η εφαρμογή Element Pro απαιτείται στο %1$s. Παρακαλώ κατεβάστε την από το κατάστημα."</string>
|
||||
<string name="screen_change_server_error_element_pro_required_title">"Απαιτείται το Element Pro"</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"Δεν μπορούσαμε να επικοινωνήσουμε με αυτόν τον οικιακό διακομιστή. Βεβαιώσου ότι έχεις εισαγάγει σωστά τη διεύθυνση URL του αρχικού διακομιστή. Εάν η διεύθυνση URL είναι σωστή, επικοινώνησε με τον διαχειριστή του κεντρικού διακομιστή για περαιτέρω βοήθεια."</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"Δεν ήταν δυνατή η επικοινωνία με αυτόν τον πάροχο λογαριασμού. Ελέγξτε ότι έχετε εισαγάγει σωστά τη διεύθυνση URL του παρόχου λογαριασμού. Εάν η διεύθυνση URL είναι σωστή, επικοινωνήστε με τον διαχειριστή του παρόχου λογαριασμού σας για περαιτέρω βοήθεια."</string>
|
||||
<string name="screen_change_server_error_invalid_well_known">"Ο διακομιστής δεν είναι διαθέσιμος λόγω προβλήματος στο αρχείο .well-known:
|
||||
%1$s"</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"Ο επιλεγμένος πάροχος λογαριασμού δεν υποστηρίζει sliding sync. Απαιτείται αναβάθμιση στο διακομιστή για χρήση του %1$s."</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"Ο επιλεγμένος πάροχος λογαριασμού δεν υποστηρίζει sliding sync. Απαιτείται αναβάθμιση του διακομιστή για χρήση του %1$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver">"%1$s δεν επιτρέπεται να συνδεθεί με %2$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_content">"Αυτή η εφαρμογή έχει ρυθμιστεί ώστε να επιτρέπει: %1$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_title">"Ο πάροχος %1$s λογαριασμού δεν επιτρέπεται."</string>
|
||||
@@ -32,7 +32,7 @@
|
||||
<string name="screen_login_error_invalid_credentials">"Λανθασμένο όνομα χρήστη ή κωδικός πρόσβασης"</string>
|
||||
<string name="screen_login_error_invalid_user_id">"Αυτό δεν είναι έγκυρο αναγνωριστικό χρήστη. Αναμενόμενη μορφή: \'@χρήστης:homeserver.org\'"</string>
|
||||
<string name="screen_login_error_refresh_tokens">"Αυτός ο διακομιστής έχει ρυθμιστεί ώστε να χρησιμοποιεί διακριτικά ανανέωσης. Αυτά δεν υποστηρίζονται όταν χρησιμοποιείς σύνδεση μέσω κωδικού πρόσβασης."</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"Ο επιλεγμένος οικιακός διακομιστής δεν υποστηρίζει κωδικό πρόσβασης ή σύνδεση OAuth. Επικοινωνήστε με τον διαχειριστή σου ή επέλεξε άλλο οικιακό διακομιστή."</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"Ο επιλεγμένος πάροχος λογαριασμού δεν υποστηρίζει σύνδεση με κωδικό πρόσβασης ή OAuth. Παρακαλούμε επικοινωνήστε με τον πάροχο του λογαριασμού σας ή επιλέξτε διαφορετικό."</string>
|
||||
<string name="screen_login_form_header">"Εισήγαγε τα στοιχεία σου"</string>
|
||||
<string name="screen_login_subtitle">"Το Matrix είναι ένα ανοιχτό δίκτυο για ασφαλή, αποκεντρωμένη επικοινωνία."</string>
|
||||
<string name="screen_login_title">"Καλωσόρισες ξανά!"</string>
|
||||
@@ -81,7 +81,7 @@
|
||||
<string name="screen_qr_code_login_error_sliding_sync_not_supported_title">"Το %1$s δεν υποστηρίζεται"</string>
|
||||
<string name="screen_qr_code_login_initial_state_button_title">"Έτοιμο για σάρωση"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_1">"Άνοιγμα %1$s σε υπολογιστή"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_2">"Κάνε κλικ στο avatar σου"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_2">"Κάντε κλικ στο προφίλ σας"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_3">"Επιλογή %1$s"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_3_action">"«Σύνδεση νέας συσκευής»"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_4">"Σάρωσε τον κωδικό QR με αυτήν τη συσκευή"</string>
|
||||
|
||||
@@ -89,7 +89,7 @@ Proovi käsitsi sisselogimist või skaneeri QR-koodi mõne muu seadmega."</strin
|
||||
<string name="screen_qr_code_login_error_sliding_sync_not_supported_title">"%1$s pole toetatud"</string>
|
||||
<string name="screen_qr_code_login_initial_state_button_title">"Skaneerimiseks valmis"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_1">"Ava %1$s töölauarakenduses"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_2">"Klõpsi oma tunnuspilti"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_2">"Klõpsa oma tunnuspilti"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_3">"Vali %1$s"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_3_action">"„Seo uus seade“"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_4">"Skaneeri QR-koodi selle seadmega"</string>
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
<string name="screen_change_server_error_element_pro_required_action_android">"Google Play"</string>
|
||||
<string name="screen_change_server_error_element_pro_required_message">"L’application Element Pro est requise sur %1$s. Veuillez la télécharger depuis le store."</string>
|
||||
<string name="screen_change_server_error_element_pro_required_title">"Element Pro est requis"</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"Nous n’avons pas pu atteindre ce serveur d’accueil. Vérifiez que vous avez correctement saisi l’URL du serveur d’accueil. Si l’URL est correcte, contactez l’administrateur de votre serveur d’accueil pour obtenir de l’aide."</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"Impossible de se connecter à ce fournisseur de compte. Vérifiez que l’URL saisie est correcte. Si c’est le cas, contactez l’administrateur de votre fournisseur de compte pour obtenir de l’aide."</string>
|
||||
<string name="screen_change_server_error_invalid_well_known">"Ce fournisseur de compte n’est pas disponible en raison d’un problème dans le fichier .well-known:
|
||||
%1$s"</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"Le fournisseur de compte sélectionné ne prend pas en charge le sliding sync. Une mise à jour du serveur est nécessaire pour pouvoir utiliser %1$s."</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"Le fournisseur de compte sélectionné ne prend pas en charge le sliding sync. Le serveur doit être mis à jour pour pouvoir utiliser %1$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver">"%1$s n’est pas autorisé à se connecter à %2$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_content">"Cette application a été configurée pour autoriser: %1$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_title">"Le fournisseur de compte %1$s n’est pas autorisé."</string>
|
||||
@@ -40,7 +40,7 @@
|
||||
<string name="screen_login_error_invalid_credentials">"Nom d’utilisateur et/ou mot de passe incorrects"</string>
|
||||
<string name="screen_login_error_invalid_user_id">"Il ne s’agit pas d’un identifiant utilisateur valide. Format attendu : « @user:homeserver.org »"</string>
|
||||
<string name="screen_login_error_refresh_tokens">"Ce serveur est configuré pour utiliser des tokens d’actualisation. Ils ne sont pas pris en charge lors de l’utilisation d’une connexion basée sur un mot de passe."</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"Le serveur d’accueil sélectionné ne prend pas en charge le mot de passe ou la connexion OAuth. Contactez votre administrateur ou choisissez un autre serveur d’accueil."</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"Le fournisseur de compte sélectionné ne prend pas en charge la connexion avec mot de passe ni OAuth. Contactez votre administrateur ou choisissez un autre fournisseur de compte."</string>
|
||||
<string name="screen_login_form_header">"Saisissez vos identifiants"</string>
|
||||
<string name="screen_login_subtitle">"Matrix est un réseau ouvert pour une communication sécurisée et décentralisée."</string>
|
||||
<string name="screen_login_title">"Content de vous revoir !"</string>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<string name="screen_change_server_error_element_pro_required_action_android">"Google Play"</string>
|
||||
<string name="screen_change_server_error_element_pro_required_message">"Potrebna je aplikacija Element Pro na %1$s. Molimo vas da je preuzmete iz trgovine."</string>
|
||||
<string name="screen_change_server_error_element_pro_required_title">"Potreban je Element Pro"</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"Nismo mogli pristupiti ovom matičnom poslužitelju. Provjerite jeste li ispravno unijeli URL matičnog poslužitelja. Ako je URL ispravan, obratite se administratoru matičnog poslužitelja za daljnju pomoć."</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"Nismo mogli kontaktirati ovog davatelja računa. Provjerite jeste li ispravno unijeli URL davatelja računa. Ako je URL ispravan, obratite se administratoru davatelja računa za daljnju pomoć."</string>
|
||||
<string name="screen_change_server_error_invalid_well_known">"Poslužitelj nije dostupan zbog problema u .well-known datoteci:
|
||||
%1$s"</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"Odabrani davatelj usluga računa ne podržava sliding sync. Za korištenje je potrebna nadogradnja poslužitelja %1$ssliding sync."</string>
|
||||
@@ -25,14 +25,22 @@
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_title">"Davatelj usluga računa %1$s nije dopušten."</string>
|
||||
<string name="screen_change_server_form_header">"URL matičnog poslužitelja"</string>
|
||||
<string name="screen_change_server_form_notice">"Unesite adresu domene."</string>
|
||||
<string name="screen_change_server_navigation_title_login">"Prijava"</string>
|
||||
<string name="screen_change_server_navigation_title_register">"Izradi račun"</string>
|
||||
<string name="screen_change_server_subtitle">"Koja je adresa vašeg poslužitelja?"</string>
|
||||
<string name="screen_change_server_textfield_footer_login">"Ako vam je račun omogućila vaša tvrtka ili zajednica, promijenite pružatelja računa (primjer: nazivtvrtke.com)."</string>
|
||||
<string name="screen_change_server_textfield_footer_register">"Besplatno kreirajte račun na matrix.org. Ako želite kreirati račun kod drugog pružatelja usluga, promijenite ga u skladu s tim (primjer: communityname.org)."</string>
|
||||
<string name="screen_change_server_textfield_header">"Pružatelj usluga računa"</string>
|
||||
<string name="screen_change_server_textfield_placeholder">"primjer.com"</string>
|
||||
<string name="screen_change_server_title">"Odaberite svoj poslužitelj"</string>
|
||||
<string name="screen_change_server_title_login">"Unesite davatelja usluge računa"</string>
|
||||
<string name="screen_change_server_title_register">"Odaberite pružatelja usluga računa"</string>
|
||||
<string name="screen_create_account_title">"Izradi račun"</string>
|
||||
<string name="screen_login_error_deactivated_account">"Ovaj je račun deaktiviran."</string>
|
||||
<string name="screen_login_error_invalid_credentials">"Netočno korisničko ime i/ili zaporka"</string>
|
||||
<string name="screen_login_error_invalid_user_id">"To nije valjani identifikator korisnika. Očekivani oblik: ‘@korisnik:matičniposlužitelj.org’"</string>
|
||||
<string name="screen_login_error_refresh_tokens">"Ovaj je poslužitelj konfiguriran za korištenje tokena za osvježavanje. Oni nisu podržani kada se upotrebljava prijava temeljena na zaporki."</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"Odabrani matični poslužitelj ne podržava zaporku ili OAuth prijavu. Obratite se administratoru ili odaberite drugi matični poslužitelj."</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"Odabrani pružatelj usluga računa ne podržava prijavu putem lozinke ili OAuth. Obratite se pružatelju usluga računa ili odaberite drugog."</string>
|
||||
<string name="screen_login_form_header">"Unesite svoje podatke"</string>
|
||||
<string name="screen_login_subtitle">"Matrix je otvorena mreža za sigurnu, decentraliziranu komunikaciju."</string>
|
||||
<string name="screen_login_title">"Dobro došli natrag!"</string>
|
||||
|
||||
@@ -16,23 +16,31 @@
|
||||
<string name="screen_change_server_error_element_pro_required_action_android">"Google Play"</string>
|
||||
<string name="screen_change_server_error_element_pro_required_message">"L\'app Element Pro è necessaria su %1$s. Scaricala dallo store."</string>
|
||||
<string name="screen_change_server_error_element_pro_required_title">"Element Pro è richiesto"</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"Non siamo riusciti a raggiungere questo homeserver. Verifica di aver inserito correttamente l\'URL. Se l\'URL è corretto, contatta l\'amministratore del homeserver per ulteriore assistenza."</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"Non è stato possibile contattare il gestorehomeserver di questo account. Verifica di aver inserito correttamente l\'URL dell\'homeserver. Se l\'URL è corretto, contatta il gestore del tuo account per ulteriore assistenza."</string>
|
||||
<string name="screen_change_server_error_invalid_well_known">"Il server non è disponibile per un problema nel file well-known:
|
||||
%1$s"</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"Il fornitore di account selezionato non supporta la Sliding sync. Per utilizzare %1$s è necessario un aggiornamento del server."</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"Il gestore dell\'account selezionato non supporta la sliding sync. È necessario un aggiornamento per utilizzare %1$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver">"%1$s non è autorizzato a connettersi a %2$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_content">"Questa app è stata configurata per consentire: %1$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_title">"Fornitore dell\'account %1$s non consentito."</string>
|
||||
<string name="screen_change_server_form_header">"URL dell\'homeserver"</string>
|
||||
<string name="screen_change_server_form_notice">"Inserisci un indirizzo di dominio."</string>
|
||||
<string name="screen_change_server_navigation_title_login">"Accedi"</string>
|
||||
<string name="screen_change_server_navigation_title_register">"Crea account"</string>
|
||||
<string name="screen_change_server_subtitle">"Qual è l\'indirizzo del tuo server?"</string>
|
||||
<string name="screen_change_server_textfield_footer_login">"Se il tuo account è fornito dalla tua azienda o da una comunità, cambia il gestore dell\'account (esempio: companyname.com)."</string>
|
||||
<string name="screen_change_server_textfield_footer_register">"Crea un account gratuito su matrix.org. Se desideri creare un account con un provider diverso, modificalo di conseguenza (esempio: communityname.org)."</string>
|
||||
<string name="screen_change_server_textfield_header">"Gestore dell\'account"</string>
|
||||
<string name="screen_change_server_textfield_placeholder">"esempio.com"</string>
|
||||
<string name="screen_change_server_title">"Seleziona il tuo server"</string>
|
||||
<string name="screen_change_server_title_login">"Inserisci il tuo gestore dell\'account"</string>
|
||||
<string name="screen_change_server_title_register">"Scegli un gestore dell\'account"</string>
|
||||
<string name="screen_create_account_title">"Crea account"</string>
|
||||
<string name="screen_login_error_deactivated_account">"Questo account è stato eliminato."</string>
|
||||
<string name="screen_login_error_invalid_credentials">"Nome utente e/o password errati"</string>
|
||||
<string name="screen_login_error_invalid_user_id">"Questo non è un identità utente valida. il formato atteso é: \'@user:homeserver.org\'"</string>
|
||||
<string name="screen_login_error_refresh_tokens">"Questo server è configurato per usare i token di aggiornamento. Non sono supportati quando si usa l\'accesso basato su password."</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"L\'homeserver selezionato non supporta la password o l\'accesso OAuth. Contatta il tuo amministratore o scegli un altro homeserver."</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"Il gestore dell\'account selezionato non supporta l\'accesso tramite password o OAuth. Si prega di contattare il proprio gestore dell\'account o di sceglierne uno diverso."</string>
|
||||
<string name="screen_login_form_header">"Inserisci i tuoi dati"</string>
|
||||
<string name="screen_login_subtitle">"Matrix è una rete aperta per comunicazioni sicure e decentralizzate."</string>
|
||||
<string name="screen_login_title">"Bentornato!"</string>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<string name="screen_onboarding_welcome_back">"다시 오신 것을 환영합니다"</string>
|
||||
<string name="screen_onboarding_welcome_message">"%1$s 에 오신 것을 환영합니다. 속도와 단순성을 극대화한 가장 빠른 버전입니다."</string>
|
||||
<string name="screen_onboarding_welcome_subtitle">"%1$s 에 오신 것을 환영합니다. 속도와 단순성을 위해 최적화된 앱입니다."</string>
|
||||
<string name="screen_onboarding_welcome_title">"당신의 엘리먼트에 있어"</string>
|
||||
<string name="screen_onboarding_welcome_title">"가장 나다운 공간, Element에서"</string>
|
||||
<string name="screen_qr_code_login_connecting_subtitle">"안전한 연결 설정"</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_description">"새 장치에 안전하게 연결할 수 없습니다. 기존 장치는 여전히 안전하므로 걱정할 필요가 없습니다."</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_list_header">"이제 어떻게 해야 할까?"</string>
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
<string name="screen_change_server_error_element_pro_required_action_android">"Google Play"</string>
|
||||
<string name="screen_change_server_error_element_pro_required_message">"Wymagana jest aplikacja Element Pro na %1$s. Znajdziesz ją w sklepie z aplikacjami."</string>
|
||||
<string name="screen_change_server_error_element_pro_required_title">"Wymagany jest Element Pro"</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"Nie mogliśmy połączyć się z tym serwerem domowym. Sprawdź, czy adres URL serwera został wprowadzony poprawnie. Jeśli adres URL jest poprawny, skontaktuj się z administratorem serwera w celu uzyskania dalszej pomocy."</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"Nie mogliśmy połączyć się z dostawcą konta. Sprawdź, czy adres URL serwera został wprowadzony poprawnie. Jeśli adres URL jest poprawny, skontaktuj się z administratorem dostawcy konta w celu uzyskania dalszej pomocy."</string>
|
||||
<string name="screen_change_server_error_invalid_well_known">"Serwer nie jest dostępny z powodu problemu pliku .well-known:
|
||||
%1$s"</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"Wybrany dostawca konta nie wspiera synchronizacji przesuwnej. Wymagana jest aktualizacja serwera %1$s."</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"Wybrany dostawca konta nie wspiera funkcji sliding sync. Wymagana jest aktualizacja serwera, aby korzystać z %1$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver">"%1$s nie posiada zezwolenia na dołączenie do %2$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_content">"Aplikacja została skonfigurowana tak, aby zezwalała na: %1$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_title">"Dostawca konta %1$s jest niedozwolony."</string>
|
||||
@@ -40,7 +40,7 @@
|
||||
<string name="screen_login_error_invalid_credentials">"Nieprawidłowa nazwa użytkownika i/lub hasło"</string>
|
||||
<string name="screen_login_error_invalid_user_id">"To nie jest prawidłowy identyfikator użytkownika. Oczekiwany format: \'@user:homeserver.org\'"</string>
|
||||
<string name="screen_login_error_refresh_tokens">"Ten serwer został skonfigurowany do korzystania z tokenów odświeżania. Nie są one obsługiwane, gdy korzystasz z hasła."</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"Wybrany serwer domowy nie obsługuje uwierzytelniania hasłem, ani OAuth. Skontaktuj się z jego administratorem lub wybierz inny serwer domowy."</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"Wybrany dostawca konta nie obsługuje uwierzytelniania hasłem, ani OAuth. Spróbuj się z nim skontaktować lub wybierz innego."</string>
|
||||
<string name="screen_login_form_header">"Wprowadź swoje dane"</string>
|
||||
<string name="screen_login_subtitle">"Matrix to otwarta sieć do bezpiecznej i zdecentralizowanej komunikacji."</string>
|
||||
<string name="screen_login_title">"Witaj ponownie!"</string>
|
||||
|
||||
@@ -16,17 +16,25 @@
|
||||
<string name="screen_change_server_error_element_pro_required_action_android">"Google Play"</string>
|
||||
<string name="screen_change_server_error_element_pro_required_message">"Требуется приложение Element Pro для %1$s. Пожалуйста, загрузите его из магазина."</string>
|
||||
<string name="screen_change_server_error_element_pro_required_title">"Требуется Element Pro"</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"Нам не удалось связаться с этим сервером. Убедитесь, что вы правильно указали адрес сервера. Если адрес указан правильно, обратитесь к администратору сервера за дополнительной помощью."</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"Нам не удалось связаться с этим сервером. Убедитесь, что вы правильно указали адрес сервера. Если адрес указан правильно, обратитесь к администратору сервера за дополнительной помощью. Также возможно, что сервер заблокирован, в таком случае Вам нужно сменить сервер."</string>
|
||||
<string name="screen_change_server_error_invalid_well_known">"Сервер недоступен из-за проблемы в файле .well-known:
|
||||
%1$s"</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"Выбранный сервер не поддерживает Sliding sync. Для использования %1$s необходимо обновление сервера."</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"Выбранный сервер не поддерживает Sliding Sync. Для использования %1$s необходимо обновление сервера."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver">"%1$s отказано в подключении к %2$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_content">"Это приложение настроено таким образом, чтобы разрешить: %1$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_title">"Сервер %1$s не разрешен."</string>
|
||||
<string name="screen_change_server_form_header">"Адрес сервера"</string>
|
||||
<string name="screen_change_server_form_notice">"Введите доменное имя."</string>
|
||||
<string name="screen_change_server_navigation_title_login">"Войти"</string>
|
||||
<string name="screen_change_server_navigation_title_register">"Создать аккаунт"</string>
|
||||
<string name="screen_change_server_subtitle">"Какой адрес у вашего сервера?"</string>
|
||||
<string name="screen_change_server_textfield_footer_login">"Если Ваш аккаунт предоставлен Вашей компанией или сообществом, смените сервер (example: companyname.com)."</string>
|
||||
<string name="screen_change_server_textfield_footer_register">"Создайте аккаунт бесплатно на matrix.org. Если Вы хотите создать аккаунт на другом сервере, смените сервер здесь (например: communityname.org)."</string>
|
||||
<string name="screen_change_server_textfield_header">"Сервер"</string>
|
||||
<string name="screen_change_server_textfield_placeholder">"example.com"</string>
|
||||
<string name="screen_change_server_title">"Выберите свой сервер"</string>
|
||||
<string name="screen_change_server_title_login">"Введите сервер"</string>
|
||||
<string name="screen_change_server_title_register">"Выберите сервер"</string>
|
||||
<string name="screen_create_account_title">"Создать аккаунт"</string>
|
||||
<string name="screen_login_error_deactivated_account">"Эта учетная запись была удалена."</string>
|
||||
<string name="screen_login_error_invalid_credentials">"Неверное имя пользователя и/или пароль"</string>
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
<string name="screen_change_server_error_element_pro_required_action_android">"Google Play"</string>
|
||||
<string name="screen_change_server_error_element_pro_required_message">"Необхідно встановити застосунок Element Pro на %1$s. Завантажте його з магазину."</string>
|
||||
<string name="screen_change_server_error_element_pro_required_title">"Потрібен Element Pro"</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"Не вдалося під\'єднатися до цього домашнього сервера. Перевірте правильність введеної URL-адреси домашнього сервера. Якщо URL-адреса правильна, зверніться по додаткову допомогу до адміністратора домашнього сервера."</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"Нам не вдалося зв’язатися з цим постачальником облікового запису. Перевірте, чи правильно ви ввели URL-адресу постачальника облікового запису. Якщо URL-адреса правильна, зверніться до адміністратора постачальника вашого облікового запису по додаткову допомогу."</string>
|
||||
<string name="screen_change_server_error_invalid_well_known">"Сервер недоступний через помилку у файлі well-known:
|
||||
%1$s"</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"Вибраний постачальник облікових записів не підтримує sliding sync. Щоб користуватися %1$s необхідно оновити сервер."</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"Вибраний постачальник облікового запису не підтримує Sliding sync. Для використання функції %1$s потрібно оновити сервер."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver">"%1$s не дозволено під\'єднуватися до %2$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_content">"Цей застосунок налаштовано так, щоб дозволити: %1$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_title">"Постачальник облікового запису %1$s не дозволений."</string>
|
||||
@@ -40,7 +40,7 @@
|
||||
<string name="screen_login_error_invalid_credentials">"Неправильне ім\'я користувача та/або пароль"</string>
|
||||
<string name="screen_login_error_invalid_user_id">"Це недійсний ідентифікатор користувача. Очікуваний формат: \'@user:homeserver.org\'"</string>
|
||||
<string name="screen_login_error_refresh_tokens">"Цей сервер налаштований на використання оновлюваних токенів. Вони не підтримуються, якщо використовується вхід за допомогою основі пароля."</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"Обраний домашній сервер не підтримує вхід за допомогою пароля або OAuth. Зверніться до адміністратора або виберіть інший домашній сервер."</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"Вибраний постачальник облікового запису не підтримує пароль або вхід через OAuth. Зверніться до постачальника облікового запису або виберіть іншого."</string>
|
||||
<string name="screen_login_form_header">"Введіть свої дані"</string>
|
||||
<string name="screen_login_subtitle">"Matrix — це відкрита мережа для безпечної, децентралізованої комунікації."</string>
|
||||
<string name="screen_login_title">"З поверненням!"</string>
|
||||
@@ -89,7 +89,7 @@
|
||||
<string name="screen_qr_code_login_error_sliding_sync_not_supported_title">"%1$s не підтримується"</string>
|
||||
<string name="screen_qr_code_login_initial_state_button_title">"Готовий до сканування"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_1">"Відкрийте %1$s на комп\'ютері"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_2">"Натисніть на свою аватарку"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_2">"Натисніть на свій аватар"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_3">"Виберіть %1$s"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_3_action">"“Під\'єднати новий пристрій”"</string>
|
||||
<string name="screen_qr_code_login_initial_state_item_4">"Зіскануйте QR-код цим пристроєм"</string>
|
||||
|
||||
@@ -25,8 +25,16 @@
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_title">"不允許帳號提供者 %1$s。"</string>
|
||||
<string name="screen_change_server_form_header">"家伺服器 URL"</string>
|
||||
<string name="screen_change_server_form_notice">"輸入網域位址。"</string>
|
||||
<string name="screen_change_server_navigation_title_login">"登入"</string>
|
||||
<string name="screen_change_server_navigation_title_register">"建立帳號"</string>
|
||||
<string name="screen_change_server_subtitle">"您的伺服器地址?"</string>
|
||||
<string name="screen_change_server_textfield_footer_login">"若您的帳號是由您的公司或社群提供的,請變更帳號提供者(範例:companyname.com)。"</string>
|
||||
<string name="screen_change_server_textfield_footer_register">"在 matrix.org 上建立免費帳號。若您想要使用其他提供者建立帳號,請據此調整(範例:communityname.org)。"</string>
|
||||
<string name="screen_change_server_textfield_header">"帳號提供者"</string>
|
||||
<string name="screen_change_server_textfield_placeholder">"example.com"</string>
|
||||
<string name="screen_change_server_title">"選擇您的伺服器"</string>
|
||||
<string name="screen_change_server_title_login">"輸入您的帳號提供者"</string>
|
||||
<string name="screen_change_server_title_register">"選擇帳號提供者"</string>
|
||||
<string name="screen_create_account_title">"建立帳號"</string>
|
||||
<string name="screen_login_error_deactivated_account">"此帳號已被刪除。"</string>
|
||||
<string name="screen_login_error_invalid_credentials">"不正確的使用者名稱或密碼"</string>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<string name="screen_change_server_error_element_pro_required_action_android">"Google Play"</string>
|
||||
<string name="screen_change_server_error_element_pro_required_message">"%1$s 要求 Element Pro。请从应用商店下载。"</string>
|
||||
<string name="screen_change_server_error_element_pro_required_title">"需要 Element Pro"</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"我们无法访问此服务器。请检查输入的服务器 URL 是否正确。如果 URL 正确,请联系服务器管理员寻求进一步帮助。"</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"我们无法访问此账户提供者。请检查输入的账户提供者 URL 是否正确。如果 URL 正确,请联系账户提供者管理员寻求进一步帮助。"</string>
|
||||
<string name="screen_change_server_error_invalid_well_known">"由于 .well-known 文件存在问题,服务器不可用:
|
||||
%1$s"</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"所选账户提供者不支持滑动同步。需要升级服务器才能使用 %1$s。"</string>
|
||||
@@ -40,7 +40,7 @@
|
||||
<string name="screen_login_error_invalid_credentials">"用户名与(或)密码不正确"</string>
|
||||
<string name="screen_login_error_invalid_user_id">"这不是合法的用户 ID。预期格式:“@user:homeserver.org”。"</string>
|
||||
<string name="screen_login_error_refresh_tokens">"此服务器使用刷新令牌。使用密码登录时不支持这些功能。"</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"该服务器不支持密码登录与 OAuth 登录。请联系服务器管理员或选择另一服务器。"</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"所选账户提供者不支持密码或 OAuth 登录。请联系账户提供者或选择另一账户提供者。"</string>
|
||||
<string name="screen_login_form_header">"输入详细信息"</string>
|
||||
<string name="screen_login_subtitle">"Matrix 是一个用于安全、去中心化通信的开放网络。"</string>
|
||||
<string name="screen_login_title">"欢迎回来!"</string>
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
<string name="screen_change_server_error_element_pro_required_action_android">"Google Play"</string>
|
||||
<string name="screen_change_server_error_element_pro_required_message">"The Element Pro app is required on %1$s. Please download it from the store."</string>
|
||||
<string name="screen_change_server_error_element_pro_required_title">"Element Pro required"</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"We couldn\'t reach this homeserver. Please check that you have entered the homeserver URL correctly. If the URL is correct, contact your homeserver administrator for further help."</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"We couldn\'t reach this account provider. Please check that you have entered the account provider URL correctly. If the URL is correct, contact your account provider administrator for further help."</string>
|
||||
<string name="screen_change_server_error_invalid_well_known">"Server isn\'t available due to an issue in the .well-known file:
|
||||
%1$s"</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"The selected account provider does not support sliding sync. An upgrade to the server is needed to use %1$s."</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"The selected account provider does not support sliding sync. The server needs to be upgraded to use %1$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver">"%1$s is not allowed to connect to %2$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_content">"This app has been configured to allow: %1$s."</string>
|
||||
<string name="screen_change_server_error_unauthorized_homeserver_title">"Account provider %1$s not allowed."</string>
|
||||
@@ -40,7 +40,7 @@
|
||||
<string name="screen_login_error_invalid_credentials">"Incorrect username and/or password"</string>
|
||||
<string name="screen_login_error_invalid_user_id">"This is not a valid user identifier. Expected format: ‘@user:homeserver.org’"</string>
|
||||
<string name="screen_login_error_refresh_tokens">"This server is configured to use refresh tokens. These aren\'t supported when using password based login."</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"The selected homeserver doesn\'t support password or OAuth login. Please contact your admin or choose another homeserver."</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"The selected account provider doesn\'t support password or OAuth login. Please contact your account provider or choose a different one."</string>
|
||||
<string name="screen_login_form_header">"Enter your details"</string>
|
||||
<string name="screen_login_subtitle">"Matrix is an open network for secure, decentralised communication."</string>
|
||||
<string name="screen_login_title">"Welcome back!"</string>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user