mirror of
https://github.com/bitfireAT/davx5-ose.git
synced 2025-12-30 18:37:49 -05:00
Compare commits
303 Commits
debug-buil
...
v1.9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20ec2c8989 | ||
|
|
d9a0888159 | ||
|
|
a267a1c5bb | ||
|
|
41001579c7 | ||
|
|
19f842421a | ||
|
|
b5c8be8c58 | ||
|
|
b43cdd6080 | ||
|
|
f9d7f05b90 | ||
|
|
af0fb62981 | ||
|
|
84acacf2e0 | ||
|
|
1db0f4985d | ||
|
|
9b965a41d6 | ||
|
|
8f115c0552 | ||
|
|
0e72810fd0 | ||
|
|
0db7b2052e | ||
|
|
7e724a709b | ||
|
|
f3e23792bd | ||
|
|
a8658890a0 | ||
|
|
5e8d0a6ae1 | ||
|
|
c81ba88557 | ||
|
|
17c839da17 | ||
|
|
ca11048f1a | ||
|
|
7ecc8c63f3 | ||
|
|
876fb92ca0 | ||
|
|
a2ead1957a | ||
|
|
962169b42c | ||
|
|
ce2ad5b98d | ||
|
|
510552639d | ||
|
|
d9a7524c32 | ||
|
|
853e1649b4 | ||
|
|
ac8b2a31ed | ||
|
|
55c09e00f2 | ||
|
|
d6a63b4bb3 | ||
|
|
08602a018c | ||
|
|
be804a3fe8 | ||
|
|
b4459fc2cf | ||
|
|
7d0e52e1b9 | ||
|
|
6ab9e4ca44 | ||
|
|
673765bd32 | ||
|
|
0e01070089 | ||
|
|
17514ae584 | ||
|
|
bea37580cc | ||
|
|
ba0f0bac49 | ||
|
|
d0a13a289c | ||
|
|
830488b848 | ||
|
|
434f9f8223 | ||
|
|
2fd29aa823 | ||
|
|
1cbc6d0483 | ||
|
|
83563aab32 | ||
|
|
2ed5112da6 | ||
|
|
e7978edbe0 | ||
|
|
433588447d | ||
|
|
1096db490e | ||
|
|
d8aebbeea1 | ||
|
|
ae3b89b685 | ||
|
|
787e6bae1d | ||
|
|
5b1c7e85c7 | ||
|
|
5bfda85aa1 | ||
|
|
4437ca79e7 | ||
|
|
6958a5da1a | ||
|
|
520f17562e | ||
|
|
22a81d4aad | ||
|
|
a7652e404b | ||
|
|
ad16a43447 | ||
|
|
de7cce7be0 | ||
|
|
20ccc0f8ca | ||
|
|
717dd3d898 | ||
|
|
17a6bbee6f | ||
|
|
94b2b81fee | ||
|
|
bca30e8457 | ||
|
|
423231eb46 | ||
|
|
bacf84aeff | ||
|
|
a1ce99499b | ||
|
|
cda4cf1195 | ||
|
|
5872979f72 | ||
|
|
62a01fac60 | ||
|
|
cd2d410be5 | ||
|
|
d3495fd4dc | ||
|
|
cf5f9c571d | ||
|
|
748316a419 | ||
|
|
3dce04da42 | ||
|
|
f26464b5ba | ||
|
|
a3f4fd0bb5 | ||
|
|
1c5c947d0f | ||
|
|
6e5e0efbd5 | ||
|
|
c2dc66cea1 | ||
|
|
941343e34d | ||
|
|
2ce3268acd | ||
|
|
5f513e9264 | ||
|
|
fcd38a242d | ||
|
|
dfd5a81520 | ||
|
|
2138fbf0b1 | ||
|
|
77c3125d05 | ||
|
|
90fa5a3687 | ||
|
|
60162c176c | ||
|
|
ebc821cbab | ||
|
|
f54c0affbc | ||
|
|
247fd04f13 | ||
|
|
83708b6127 | ||
|
|
821ff102c3 | ||
|
|
db6fc1330e | ||
|
|
b33bf50b49 | ||
|
|
ed8a2b1e06 | ||
|
|
3df17a0b90 | ||
|
|
0961b2081b | ||
|
|
9b5be6d993 | ||
|
|
848dedef0c | ||
|
|
975932e70e | ||
|
|
bfe68f06ec | ||
|
|
211600b994 | ||
|
|
341cd3396d | ||
|
|
4ed38df9df | ||
|
|
4e599d2d48 | ||
|
|
42f0c807b1 | ||
|
|
b9a555f88c | ||
|
|
13c7623588 | ||
|
|
b1d31694d6 | ||
|
|
016b67a6f5 | ||
|
|
55645d7c05 | ||
|
|
66f08db8c0 | ||
|
|
c2707c4475 | ||
|
|
b405767623 | ||
|
|
dc17e80347 | ||
|
|
fd51954682 | ||
|
|
76d33713ab | ||
|
|
ac8542dc25 | ||
|
|
aa3d648cef | ||
|
|
76743b6636 | ||
|
|
e99082dc98 | ||
|
|
c054e0bfb5 | ||
|
|
b2d392ac30 | ||
|
|
84cc8be288 | ||
|
|
fd2ef0b7b0 | ||
|
|
492b4ec741 | ||
|
|
13304cb995 | ||
|
|
5247557872 | ||
|
|
eb14f03475 | ||
|
|
6e60503a22 | ||
|
|
6541c5d300 | ||
|
|
55b915ad3f | ||
|
|
115a481b30 | ||
|
|
312a385000 | ||
|
|
a51cd1ad21 | ||
|
|
1dee4b0d44 | ||
|
|
35d540326d | ||
|
|
c2bd7016bb | ||
|
|
35ac80cdaf | ||
|
|
f167512f74 | ||
|
|
109d62936a | ||
|
|
1611f64334 | ||
|
|
edfbdfc6cd | ||
|
|
976ddd855b | ||
|
|
c31a47b202 | ||
|
|
6f171d7b11 | ||
|
|
78a71b7da7 | ||
|
|
95912d6a9c | ||
|
|
9d642d7a49 | ||
|
|
87186a8b51 | ||
|
|
b6813df8ba | ||
|
|
74e753db49 | ||
|
|
9dc6ba634d | ||
|
|
617e8f77a6 | ||
|
|
12ead7bea7 | ||
|
|
72b4f7aba1 | ||
|
|
fcbd56ab8f | ||
|
|
5996aa2e56 | ||
|
|
3d127eb728 | ||
|
|
658f286636 | ||
|
|
061071b1af | ||
|
|
5070511476 | ||
|
|
72a24cd2d7 | ||
|
|
411698e747 | ||
|
|
6cf7f424a8 | ||
|
|
e3670feb29 | ||
|
|
fb66dc1cd3 | ||
|
|
bc8bd18db4 | ||
|
|
38d7d50c71 | ||
|
|
3dbfcab377 | ||
|
|
687bf0a597 | ||
|
|
cb5c24f37e | ||
|
|
1b05435144 | ||
|
|
e57a1a7c24 | ||
|
|
5bfdb35cb8 | ||
|
|
288ed380c3 | ||
|
|
fb9a699694 | ||
|
|
dd760f5b90 | ||
|
|
a4513f3229 | ||
|
|
e694d4ad11 | ||
|
|
9e91acbd5c | ||
|
|
a524eccdc6 | ||
|
|
d0541904a0 | ||
|
|
4e2b8b9202 | ||
|
|
750984f397 | ||
|
|
a3ab5c0f0e | ||
|
|
5ad4139100 | ||
|
|
081df97813 | ||
|
|
a2a80a3986 | ||
|
|
92dd7df51c | ||
|
|
fcbedd744a | ||
|
|
4a73328350 | ||
|
|
f81c6a782e | ||
|
|
1fe3937bce | ||
|
|
352e35d40c | ||
|
|
6c13f791ea | ||
|
|
46b1f21393 | ||
|
|
ed09c1f986 | ||
|
|
5b8caaaa66 | ||
|
|
f02660aa1f | ||
|
|
6b0bd2e88d | ||
|
|
05011a0b28 | ||
|
|
ad019185ba | ||
|
|
8c91915ff1 | ||
|
|
fcf8537db3 | ||
|
|
16070be0c9 | ||
|
|
47b7ae0420 | ||
|
|
6cde48346e | ||
|
|
7248f84c4f | ||
|
|
d065737bb0 | ||
|
|
5297b0fc3c | ||
|
|
1a8612b9e6 | ||
|
|
33a43f61d2 | ||
|
|
6211c9b539 | ||
|
|
4b52f746a0 | ||
|
|
7cc402e2dd | ||
|
|
dc26873430 | ||
|
|
87f1da5a0f | ||
|
|
2eb9c7b883 | ||
|
|
2acacf2736 | ||
|
|
1ce1bd275c | ||
|
|
6e55b44257 | ||
|
|
be816f2eee | ||
|
|
0d22ff3e52 | ||
|
|
194e47ac49 | ||
|
|
3f276d2abe | ||
|
|
12565986fd | ||
|
|
02ee844823 | ||
|
|
1091fd1472 | ||
|
|
39a0c9dcb0 | ||
|
|
3550176f99 | ||
|
|
2ca8328b7d | ||
|
|
d889d9a200 | ||
|
|
47258bee3d | ||
|
|
f2b8977a09 | ||
|
|
e2bb514331 | ||
|
|
fc3219988e | ||
|
|
acc7a2bd57 | ||
|
|
6ba68a331d | ||
|
|
c17c0aa385 | ||
|
|
06d7180972 | ||
|
|
9eb1be6de6 | ||
|
|
e5ce5eff03 | ||
|
|
5af48bb6ef | ||
|
|
644f7ef521 | ||
|
|
c4044fa0ad | ||
|
|
f0c12ce78a | ||
|
|
9bb85866b2 | ||
|
|
eb5b35ce56 | ||
|
|
9cd3304011 | ||
|
|
009e864aa9 | ||
|
|
d05985c259 | ||
|
|
f9d3f94c2d | ||
|
|
81c4ad75f1 | ||
|
|
7ea11fe351 | ||
|
|
37e0848e63 | ||
|
|
85ed6a52ae | ||
|
|
3d85d228a1 | ||
|
|
847aa6be92 | ||
|
|
41cc5bd5e2 | ||
|
|
1fe2b6d5c3 | ||
|
|
7da10249e2 | ||
|
|
a8f1114b9c | ||
|
|
318f3aad45 | ||
|
|
1fddbc8fad | ||
|
|
5a9a93e1ae | ||
|
|
b430389f46 | ||
|
|
d73fced009 | ||
|
|
decee792ab | ||
|
|
d50b3a551b | ||
|
|
8753e80a13 | ||
|
|
cbdce8df8e | ||
|
|
be5318f2b9 | ||
|
|
274ac5289e | ||
|
|
ce5b64a55c | ||
|
|
c6a9e3f7a6 | ||
|
|
27b42f0686 | ||
|
|
8700d0d893 | ||
|
|
939ae6a5ed | ||
|
|
de4633ae07 | ||
|
|
76c331a68b | ||
|
|
b4ece96a76 | ||
|
|
b51a9c2be2 | ||
|
|
0adacff51b | ||
|
|
c82150493c | ||
|
|
9c2b965ac0 | ||
|
|
b914a5d8c2 | ||
|
|
5281bc6ce0 | ||
|
|
fa4da093cd | ||
|
|
1d2692a257 | ||
|
|
cae67244d3 | ||
|
|
291f965cd7 | ||
|
|
cbb89ffecb | ||
|
|
709f299aa8 | ||
|
|
0b2abd50c7 |
4
.github/FUNDING.yml
vendored
4
.github/FUNDING.yml
vendored
@@ -1,4 +0,0 @@
|
||||
|
||||
github: bitfireAT
|
||||
liberapay: DAVx5
|
||||
custom: 'https://www.davx5.com/donate'
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: DAVx⁵ Community Support
|
||||
url: https://github.com/bitfireAT/davx5-ose/discussions
|
||||
about: Ask and answer questions (including feature requests and bug reports) here.
|
||||
43
.github/ISSUE_TEMPLATE/qualified-bug.yml
vendored
43
.github/ISSUE_TEMPLATE/qualified-bug.yml
vendored
@@ -1,43 +0,0 @@
|
||||
name: Qualified Bug Report
|
||||
description: "[Developers only] For qualified bug reports. (Use Discussions if unsure.)"
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Problem scope
|
||||
description: Use Discussions if you're unsure which component (DAVx⁵, calendar app, server, …) causes your problem.
|
||||
options:
|
||||
- label: I'm sure that this is a DAVx⁵ problem.
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: App version
|
||||
options:
|
||||
- label: I'm using the latest available DAVx⁵ version.
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Android version and device/firmware type
|
||||
placeholder: "Android 13 (Samsung A32)"
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Provide detailed steps to reproduce the problem.
|
||||
placeholder: |
|
||||
1. Create DAVx⁵ account with Some Server (Version).
|
||||
2. Sync Some Calendar.
|
||||
3. SomeException appears.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Actual result
|
||||
description: Describe what you DAVx⁵ currently does (and what is not expected).
|
||||
placeholder: "Some Property in ICS file causes the whole synchronization to stop."
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected result
|
||||
description: Describe what you would expect DAVx⁵ to avoid/solve the problem.
|
||||
placeholder: "Some Property in ICS file should be ignored even if faulty and sync should continue instead of showing an error."
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Further info
|
||||
description: Debug info, links to further information, …
|
||||
19
.github/ISSUE_TEMPLATE/qualified-feature.yml
vendored
19
.github/ISSUE_TEMPLATE/qualified-feature.yml
vendored
@@ -1,19 +0,0 @@
|
||||
name: Qualified Feature Request
|
||||
description: "[Developers only] For qualified feature requests. (Use Discussions if unsure.)"
|
||||
labels: ["enhancement"]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Scope
|
||||
description: Use this form only for features that have been discussed in Discussions or if you're a DAVx5 developer.
|
||||
options:
|
||||
- label: I'm sure that this feature request belongs here and not into Discussions.
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: Describe the requested feature and why it is desired.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Further info
|
||||
description: How this could be implemented, links to further information, …
|
||||
38
.github/pull_request_template.md
vendored
38
.github/pull_request_template.md
vendored
@@ -1,38 +0,0 @@
|
||||
|
||||
Please delete this paragraph and other repeating text (like the examples) after reading and before submitting the PR.
|
||||
|
||||
The PR should be in _Draft_ state during development. As soon as it's finished, it should be marked as _Ready for review_ and a reviewer should be chosen.
|
||||
|
||||
See also: [Writing A Great Pull Request Description](https://www.pullrequest.com/blog/writing-a-great-pull-request-description/)
|
||||
|
||||
|
||||
### Purpose
|
||||
|
||||
What this PR is intended to do and why this is desirable.
|
||||
|
||||
Example:
|
||||
|
||||
> Adds support for AAA in BBB, as requested by several people in issue #XX.
|
||||
|
||||
|
||||
### Short description
|
||||
|
||||
A short description of the chosen approach to achieve the purpose.
|
||||
|
||||
Example:
|
||||
|
||||
> - Added authentication option _Some authentication_ to _some module_.
|
||||
> - Added support for _Some authentication_ to _some content provider_.
|
||||
> - Added UI support for _Some authentication_ in account settings.
|
||||
|
||||
Related information (links to Android docs and other resources that help to understand/review
|
||||
the changes) can also be put here.
|
||||
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] The PR has a proper title, description and label.
|
||||
- [ ] I have [self-reviewed the PR](https://patrickdinh.medium.com/review-your-own-pull-requests-5634cad10b7a).
|
||||
- [ ] I have added documentation to complex functions and functions that can be used by other modules.
|
||||
- [ ] I have added reasonable tests or consciously decided to not add tests.
|
||||
|
||||
20
.github/release.yml
vendored
20
.github/release.yml
vendored
@@ -1,20 +0,0 @@
|
||||
changelog:
|
||||
exclude:
|
||||
labels:
|
||||
- ignore-for-release
|
||||
categories:
|
||||
- title: New features
|
||||
labels:
|
||||
- enhancement
|
||||
- title: Bug fixes
|
||||
labels:
|
||||
- bug
|
||||
- title: Refactoring
|
||||
labels:
|
||||
- refactoring
|
||||
- title: Dependencies
|
||||
labels:
|
||||
- dependencies
|
||||
- title: Other changes
|
||||
labels:
|
||||
- "*"
|
||||
58
.github/workflows/codeql.yml
vendored
58
.github/workflows/codeql.yml
vendored
@@ -1,58 +0,0 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main-ose ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ main-ose ]
|
||||
schedule:
|
||||
- cron: '22 10 * * 1'
|
||||
concurrency:
|
||||
group: codeql-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'java' ]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
- uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
cache-encryption-key: ${{ secrets.gradle_encryption_key }}
|
||||
cache-read-only: true # gradle user home cache is generated by test jobs
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
#- name: Autobuild
|
||||
# uses: github/codeql-action/autobuild@v2
|
||||
|
||||
- name: Build
|
||||
run: ./gradlew --build-cache --configuration-cache --configuration-cache-problems=warn --no-daemon app:assembleOseDebug
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
55
.github/workflows/dependent-issues.yml
vendored
55
.github/workflows/dependent-issues.yml
vendored
@@ -1,55 +0,0 @@
|
||||
name: Dependent Issues
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- closed
|
||||
- reopened
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- closed
|
||||
- reopened
|
||||
# Makes sure we always add status check for PRs. Useful only if
|
||||
# this action is required to pass before merging. Otherwise, it
|
||||
# can be removed.
|
||||
- synchronize
|
||||
|
||||
# Schedule a daily check. Useful if you reference cross-repository
|
||||
# issues or pull requests. Otherwise, it can be removed.
|
||||
schedule:
|
||||
- cron: '19 9 * * *'
|
||||
|
||||
permissions: write-all
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: z0al/dependent-issues@v1
|
||||
env:
|
||||
# (Required) The token to use to make API calls to GitHub.
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# (Optional) The token to use to make API calls to GitHub for remote repos.
|
||||
GITHUB_READ_TOKEN: ${{ secrets.DEPENDENT_ISSUES_READ_TOKEN }}
|
||||
|
||||
with:
|
||||
# (Optional) The label to use to mark dependent issues
|
||||
# label: dependent
|
||||
|
||||
# (Optional) Enable checking for dependencies in issues.
|
||||
# Enable by setting the value to "on". Default "off"
|
||||
check_issues: on
|
||||
|
||||
# (Optional) A comma-separated list of keywords. Default
|
||||
# "depends on, blocked by"
|
||||
keywords: depends on, blocked by
|
||||
|
||||
# (Optional) A custom comment body. It supports `{{ dependencies }}` token.
|
||||
comment: >
|
||||
This PR/issue depends on:
|
||||
|
||||
{{ dependencies }}
|
||||
48
.github/workflows/release.yml
vendored
48
.github/workflows/release.yml
vendored
@@ -1,48 +0,0 @@
|
||||
name: Create release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
prerelease: ${{ contains(github.ref_name, '-alpha') || contains(github.ref_name, '-beta') || contains(github.ref_name, '-rc') }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Create release
|
||||
permissions:
|
||||
contents: write
|
||||
discussions: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
- uses: gradle/actions/setup-gradle@v3
|
||||
|
||||
- name: Prepare keystore
|
||||
run: echo ${{ secrets.android_keystore_base64 }} | base64 -d >$GITHUB_WORKSPACE/keystore.jks
|
||||
|
||||
- name: Build signed package
|
||||
# Make sure that caches are disabled to generate reproducible release builds
|
||||
run: ./gradlew --no-build-cache --no-configuration-cache --no-daemon app:assembleRelease
|
||||
env:
|
||||
ANDROID_KEYSTORE: ${{ github.workspace }}/keystore.jks
|
||||
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.android_keystore_password }}
|
||||
ANDROID_KEY_ALIAS: ${{ secrets.android_key_alias }}
|
||||
ANDROID_KEY_PASSWORD: ${{ secrets.android_key_password }}
|
||||
|
||||
- name: Create Github release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
prerelease: ${{ env.prerelease }}
|
||||
files: app/build/outputs/apk/ose/release/*.apk
|
||||
fail_on_unmatched_files: true
|
||||
generate_release_notes: true
|
||||
discussion_category_name: Announcements
|
||||
81
.github/workflows/test-dev.yml
vendored
81
.github/workflows/test-dev.yml
vendored
@@ -1,81 +0,0 @@
|
||||
name: Development tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
concurrency:
|
||||
group: test-dev-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
name: Compile for build cache
|
||||
if: ${{ github.ref == 'refs/heads/main-ose' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
|
||||
# See https://community.gradle.org/github-actions/docs/setup-gradle/ for more information
|
||||
- uses: gradle/actions/setup-gradle@v3 # creates build cache when on main branch
|
||||
with:
|
||||
cache-encryption-key: ${{ secrets.gradle_encryption_key }}
|
||||
dependency-graph: generate-and-submit # submit Github Dependency Graph info
|
||||
|
||||
- run: ./gradlew --build-cache --configuration-cache --configuration-cache-problems=warn app:compileOseDebugSource
|
||||
|
||||
test:
|
||||
needs: compile
|
||||
if: ${{ always() }} # even if compile didn't run (because not on main branch)
|
||||
name: Lint and unit tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
- uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
cache-encryption-key: ${{ secrets.gradle_encryption_key }}
|
||||
cache-read-only: true
|
||||
|
||||
- name: Run lint
|
||||
run: ./gradlew --build-cache --configuration-cache --configuration-cache-problems=warn app:lintOseDebug
|
||||
- name: Run unit tests
|
||||
run: ./gradlew --build-cache --configuration-cache --configuration-cache-problems=warn app:testOseDebugUnitTest
|
||||
|
||||
test_on_emulator:
|
||||
needs: compile
|
||||
if: ${{ always() }} # even if compile didn't run (because not on main branch)
|
||||
name: Instrumented tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
- uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
cache-encryption-key: ${{ secrets.gradle_encryption_key }}
|
||||
cache-read-only: true
|
||||
|
||||
- name: Enable KVM group perms
|
||||
run: |
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
|
||||
- name: Cache AVD
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.config/.android/avd
|
||||
key: avd-${{ hashFiles('app/build.gradle.kts') }} # gradle-managed devices are defined there
|
||||
|
||||
- name: Run device tests
|
||||
run: ./gradlew --build-cache --configuration-cache --configuration-cache-problems=warn app:virtualCheck
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -8,9 +8,8 @@
|
||||
# Files for the Dalvik VM
|
||||
*.dex
|
||||
|
||||
# Java/Kotlin
|
||||
# Java class files
|
||||
*.class
|
||||
.kotlin/
|
||||
|
||||
# Generated files
|
||||
bin/
|
||||
|
||||
12
.gitmodules
vendored
Normal file
12
.gitmodules
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
[submodule "dav4android"]
|
||||
path = dav4android
|
||||
url = ../dav4android.git
|
||||
[submodule "ical4android"]
|
||||
path = ical4android
|
||||
url = ../ical4android.git
|
||||
[submodule "vcard4android"]
|
||||
path = vcard4android
|
||||
url = ../vcard4android.git
|
||||
[submodule "cert4android"]
|
||||
path = cert4android
|
||||
url = ../cert4android.git
|
||||
30
.tx/config
30
.tx/config
@@ -1,30 +0,0 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
lang_map = ar_SA: ar, en_GB: en-rGB, fa_IR: fa-rIR, fi_FI: fi, nb_NO: nb, sk_SK: sk, sl_SI: sl, tr_TR: tr, zh_CN: zh, zh_TW: zh-rTW
|
||||
|
||||
[o:bitfireAT:p:davx5:r:app]
|
||||
file_filter = app/src/main/res/values-<lang>/strings.xml
|
||||
source_file = app/src/main/res/values/strings.xml
|
||||
source_lang = en
|
||||
type = ANDROID
|
||||
minimum_perc = 20
|
||||
resource_name = App strings (all flavors)
|
||||
|
||||
|
||||
# Attention: fastlane directories are like "en-us", not "en-rUS"!
|
||||
|
||||
[o:bitfireAT:p:davx5:r:metadata-short-description]
|
||||
file_filter = fastlane/metadata/android/<lang>/short_description.txt
|
||||
source_file = fastlane/metadata/android/en-US/short_description.txt
|
||||
source_lang = en
|
||||
type = TXT
|
||||
minimum_perc = 100
|
||||
resource_name = Metadata: short description
|
||||
|
||||
[o:bitfireAT:p:davx5:r:metadata-full-description]
|
||||
file_filter = fastlane/metadata/android/<lang>/full_description.txt
|
||||
source_file = fastlane/metadata/android/en-US/full_description.txt
|
||||
source_lang = en
|
||||
type = TXT
|
||||
minimum_perc = 100
|
||||
resource_name = Metadata: full description
|
||||
11
AUTHORS
11
AUTHORS
@@ -1,11 +0,0 @@
|
||||
# This is the list of significant contributors to DAVx5.
|
||||
#
|
||||
# This does not necessarily list everyone who has contributed work.
|
||||
# To see the full list of contributors, see the revision history in
|
||||
# source control.
|
||||
|
||||
Ricki Hirner (bitfire.at)
|
||||
Bernhard Stockmann (bitfire.at)
|
||||
|
||||
Sunik Kupfer (bitfire.at)
|
||||
Patrick Lang (techbee.at)
|
||||
117
CONTRIBUTING.md
117
CONTRIBUTING.md
@@ -1,117 +0,0 @@
|
||||
|
||||
**Thank you for your interest in contributing to DAVx⁵!**
|
||||
|
||||
|
||||
# Licensing
|
||||
|
||||
All work in this repository is [licensed under the GPLv3](LICENSE).
|
||||
|
||||
We (bitfire.at, initial and main contributors) are also asking you to give us
|
||||
permission to use your contribution for related non-open source projects
|
||||
like [Managed DAVx⁵](https://www.davx5.com/organizations/managed-davx5).
|
||||
|
||||
If you send us a pull request, our CLA bot will ask you to sign the
|
||||
Contributor's License Agreement so that we can use your contribution.
|
||||
|
||||
|
||||
# Copyright
|
||||
|
||||
Make sure that every file that contains significant work (at least every code file)
|
||||
starts with the copyright header:
|
||||
|
||||
```
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
```
|
||||
|
||||
You can set this in Android Studio:
|
||||
|
||||
1. Settings / Editor / Copyright / Copyright Profiles
|
||||
2. Paste the text above (without the stars).
|
||||
3. Set Formatting so that the preview exactly looks like above; one blank line after the block.
|
||||
4. Set this copyright profile as the default profile for the project.
|
||||
5. Apply copyright: right-click in file tree / Update copyright.
|
||||
|
||||
|
||||
# Style guide
|
||||
|
||||
Please adhere to the [Kotlin style guide](https://developer.android.com/kotlin/style-guide) and
|
||||
the following hints to make the source code uniform.
|
||||
|
||||
**Have a look at similar files and copy their style if you're not certain.**
|
||||
|
||||
Sample file (pay attention to blank lines and other formatting):
|
||||
|
||||
```
|
||||
<Copyright header, see above>
|
||||
|
||||
class MyClass(int arg1) : SuperClass() {
|
||||
|
||||
companion object {
|
||||
|
||||
const val CONSTANT_STRING = "Constant String";
|
||||
|
||||
fun staticMethod() { // Use static methods when you don't need the object context.
|
||||
// …
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var someProperty: String = "12345"
|
||||
var someRelatedProperty: Int = 12345
|
||||
|
||||
init {
|
||||
// constructor
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use KDoc to document important methods. Don't use it dogmatically, but writing proper documentation
|
||||
* (not just the method name with spaces) helps you to re-think what the method shall really do.
|
||||
*/
|
||||
fun aFun1() { // Group methods by some logic (for instance, the order in which they will be called)
|
||||
} // and alphabetically within a group.
|
||||
|
||||
fun anotherFun() {
|
||||
// …
|
||||
}
|
||||
|
||||
|
||||
fun somethingCompletelyDifferent() { // two blank lines to separate groups
|
||||
}
|
||||
|
||||
fun helperForSomethingCompletelyDifferent() {
|
||||
someCall(arg1, arg2, arg3, arg4) // function calls: stick to one line unless it becomes confusing
|
||||
}
|
||||
|
||||
|
||||
class Model( // two blank lines before inner classes
|
||||
someArgument: SomeLongClass, // arguments in multiple lines when they're too long for one line
|
||||
anotherArgument: AnotherLongType,
|
||||
thirdArgument: AnotherLongTypeName
|
||||
) : ViewModel() {
|
||||
|
||||
fun abc() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
In general, use one blank line to separate things within one group of things, and two blank lines
|
||||
to separate groups. In rare cases, when methods are tightly coupled and are only helpers for another
|
||||
method, they may follow the calling method without separating blank lines.
|
||||
|
||||
## Tests
|
||||
|
||||
Test classes should be in the appropriate directory (see existing tests) and in the same package as the
|
||||
tested class. Tests are usually be named like `methodToBeTested_Condition()`, see
|
||||
[Test apps on Android](https://developer.android.com/training/testing/).
|
||||
|
||||
|
||||
# Authors
|
||||
|
||||
If you make significant contributions, feel free to add yourself to the [AUTHORS file](AUTHORS).
|
||||
|
||||
50
README.md
50
README.md
@@ -1,46 +1,38 @@
|
||||
|
||||
[](https://www.davx5.com/)
|
||||
[](https://f-droid.org/packages/at.bitfire.davdroid/)
|
||||
[](https://github.com/bitfireAT/davx5-ose/blob/main/LICENSE)
|
||||
[](https://fosstodon.org/@davx5app)
|
||||
[](https://github.com/bitfireAT/davx5-ose/actions/workflows/test-dev.yml)
|
||||
|
||||

|
||||
[](https://gitlab.com/bitfireAT/davdroid/commits/master)
|
||||
|
||||
|
||||
DAVx⁵
|
||||
DAVdroid
|
||||
========
|
||||
|
||||
Please see the [DAVx⁵ Web site](https://www.davx5.com) for
|
||||
comprehensive information about DAVx⁵, including a list of services it has been tested with.
|
||||
Please see the [DAVdroid Web site](https://davdroid.bitfire.at) for
|
||||
comprehensive information about DAVdroid.
|
||||
|
||||
DAVx⁵ is licensed under the [GPLv3 License](LICENSE).
|
||||
DAVdroid is licensed under the [GPLv3 License](LICENSE).
|
||||
|
||||
News and updates:
|
||||
News and updates: [@davdroidapp](https://twitter.com/davdroidapp) on Twitter /
|
||||
[davdroid-announce](https://davdroid.bitfire.at/download/newsletter/) mailing list
|
||||
|
||||
* [@davx5app@fosstodon.org](https://fosstodon.org/@davx5app) on Mastodon
|
||||
Help and discussion: [DAVdroid forums](https://davdroid.bitfire.at/forums)
|
||||
|
||||
**Help, feature requests, bug reports: [DAVx⁵ discussions](https://github.com/bitfireAT/davx5-ose/discussions)**
|
||||
**If you want to support DAVdroid, please consider [donating to DAVdroid](https://davdroid.bitfire.at/donate/)
|
||||
or [purchasing it](https://davdroid.bitfire.at/download/).**
|
||||
|
||||
Parts of DAVx⁵ have been outsourced into these libraries:
|
||||
Parts of DAVdroid have been outsourced into these libraries:
|
||||
|
||||
* [cert4android](https://github.com/bitfireAT/cert4android) – custom certificate management
|
||||
* [dav4jvm](https://github.com/bitfireAT/dav4jvm) – WebDAV/CalDav/CardDAV framework
|
||||
* [ical4android](https://github.com/bitfireAT/ical4android) – iCalendar processing and Calendar Provider access
|
||||
* [vcard4android](https://github.com/bitfireAT/vcard4android) – vCard processing and Contacts Provider access
|
||||
|
||||
**If you want to support DAVx⁵, please consider [donating to DAVx⁵](https://www.davx5.com/donate)
|
||||
or [purchasing it](https://www.davx5.com/download).**
|
||||
* [cert4android](https://gitlab.com/bitfireAT/cert4android) – custom certificate management
|
||||
* [dav4android](https://gitlab.com/bitfireAT/dav4android) – WebDAV/CalDav/CardDAV framework
|
||||
* [ical4android](https://gitlab.com/bitfireAT/ical4android) – iCalendar processing and Calendar Provider access
|
||||
* [vcard4android](https://gitlab.com/bitfireAT/vcard4android) – VCard processing and Contacts Provider access
|
||||
|
||||
|
||||
USED THIRD-PARTY LIBRARIES
|
||||
==========================
|
||||
|
||||
The most important libraries which are used by DAVx⁵ (alphabetically):
|
||||
Those libraries are used by DAVdroid (alphabetically):
|
||||
|
||||
* [dnsjava](https://github.com/dnsjava/dnsjava) – [BSD License](https://github.com/dnsjava/dnsjava/blob/master/LICENSE)
|
||||
* [ez-vcard](https://github.com/mangstadt/ez-vcard) – [New BSD License](https://github.com/mangstadt/ez-vcard/blob/master/LICENSE)
|
||||
* [iCal4j](https://github.com/ical4j/ical4j) – [New BSD License](https://github.com/ical4j/ical4j/blob/develop/LICENSE.txt)
|
||||
* [okhttp](https://square.github.io/okhttp) – [Apache License, Version 2.0](https://square.github.io/okhttp/#license)
|
||||
|
||||
See _About / Libraries_ in the app for all used libraries and their licenses.
|
||||
* [dnsjava](http://www.xbill.org/dnsjava/) – [BSD License](http://www.xbill.org/dnsjava/dnsjava-current/LICENSE)
|
||||
* [ez-vcard](https://code.google.com/p/ez-vcard/) – [New BSD License](http://opensource.org/licenses/BSD-3-Clause)
|
||||
* [iCal4j](http://ical4j.sourceforge.net/) – [New BSD License](http://sourceforge.net/p/ical4j/ical4j/ci/default/tree/LICENSE)
|
||||
* [okhttp](https://square.github.io/okhttp/) – [Apache License, Version 2.0](https://square.github.io/okhttp/#license)
|
||||
* [Project Lombok](http://projectlombok.org/) – [MIT License](http://opensource.org/licenses/mit-license.php)
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report security vulnerabilities using our [secure support form](https://www.davx5.com/support) or via email to support-en@davx5.com.
|
||||
9
app/README
Normal file
9
app/README
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
flavor directory
|
||||
|
||||
gplay main + davdroid + gplay
|
||||
icloud main + icloud
|
||||
managed main + managed
|
||||
soldupe main + soldupe
|
||||
standard main + davdroid
|
||||
|
||||
155
app/build.gradle
Normal file
155
app/build.gradle
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (c) Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
android {
|
||||
compileSdkVersion 26
|
||||
buildToolsVersion '26.0.1'
|
||||
|
||||
defaultConfig {
|
||||
applicationId "at.bitfire.davdroid"
|
||||
resValue "string", "packageID", applicationId
|
||||
|
||||
versionCode 182
|
||||
buildConfigField "long", "buildTime", System.currentTimeMillis() + "L"
|
||||
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 25
|
||||
|
||||
buildConfigField "boolean", "customCerts", "false"
|
||||
}
|
||||
|
||||
productFlavors {
|
||||
standard {
|
||||
versionName "1.9"
|
||||
buildConfigField "boolean", "customCerts", "true"
|
||||
}
|
||||
managed {
|
||||
applicationId "com.davdroid.managed"
|
||||
resValue "string", "packageID", applicationId
|
||||
minSdkVersion 21
|
||||
|
||||
versionName "1.9"
|
||||
}
|
||||
|
||||
gplay {
|
||||
versionName "1.9-gplay"
|
||||
buildConfigField "boolean", "customCerts", "true"
|
||||
}
|
||||
icloud {
|
||||
applicationId "at.bitfire.cloudsync"
|
||||
resValue "string", "packageID", applicationId
|
||||
|
||||
versionName "1.9-cloud"
|
||||
}
|
||||
soldupe {
|
||||
applicationId "com.soldupe.cloudsync"
|
||||
resValue "string", "packageID", applicationId
|
||||
minSdkVersion 21
|
||||
|
||||
versionName "1.9-soldupe"
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
standard.java.srcDirs = [ "src/davdroid/java" ]
|
||||
standard.res.srcDirs = [ "src/davdroid/res" ]
|
||||
gplay.java.srcDirs = [ "src/gplay/java", "src/davdroid/java" ]
|
||||
gplay.res.srcDirs = [ "src/gplay/res", "src/davdroid/res" ]
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
bitfire {
|
||||
storeFile file("${System.env.HOME}/Entwicklung/GooglePlay/bitfire.jks")
|
||||
storePassword '***REMOVED***'
|
||||
keyAlias 'bitfire'
|
||||
keyPassword '***REMOVED***'
|
||||
}
|
||||
soldupe {
|
||||
storeFile file("${System.env.HOME}/Entwicklung/GooglePlay/soldupe.jks")
|
||||
storePassword 'hei8eePh'
|
||||
keyAlias 'soldupe'
|
||||
keyPassword 'ocaip6oZ'
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
minifyEnabled false
|
||||
}
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
|
||||
signingConfig signingConfigs.bitfire
|
||||
productFlavors.soldupe.signingConfig signingConfigs.soldupe
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable 'GoogleAppIndexingWarning' // we don't need Google indexing, thanks
|
||||
disable 'GradleDependency'
|
||||
disable 'IconColors'
|
||||
disable 'IconLauncherShape'
|
||||
disable 'IconMissingDensityFolder'
|
||||
disable 'ImpliedQuantity', 'MissingQuantity'
|
||||
disable 'MissingTranslation', 'ExtraTranslation' // translations from Transifex are not always up to date
|
||||
disable 'Recycle' // doesn't understand Lombok's @Cleanup
|
||||
disable 'RtlEnabled'
|
||||
disable 'RtlHardcoded'
|
||||
disable 'Typos'
|
||||
}
|
||||
packagingOptions {
|
||||
exclude 'META-INF/DEPENDENCIES'
|
||||
exclude 'META-INF/LICENSE'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':cert4android')
|
||||
compile project(':dav4android')
|
||||
compile project(':ical4android')
|
||||
compile project(':vcard4android')
|
||||
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
|
||||
|
||||
compile 'com.android.support:appcompat-v7:26.0.1'
|
||||
compile 'com.android.support:cardview-v7:26.0.1'
|
||||
compile 'com.android.support:design:26.0.1'
|
||||
compile 'com.android.support:preference-v14:26.0.1'
|
||||
|
||||
compile 'com.github.yukuku:ambilwarna:2.0.1'
|
||||
|
||||
compile 'com.squareup.okhttp3:logging-interceptor:3.9.0'
|
||||
compile 'commons-io:commons-io:2.5'
|
||||
compile 'dnsjava:dnsjava:2.1.8'
|
||||
compile 'org.apache.commons:commons-lang3:3.6'
|
||||
compile 'org.apache.commons:commons-collections4:4.1'
|
||||
|
||||
// for tests
|
||||
//noinspection GradleDynamicVersion
|
||||
androidTestCompile('com.android.support.test:runner:+') {
|
||||
exclude group: 'com.android.support', module: 'support-annotations'
|
||||
}
|
||||
//noinspection GradleDynamicVersion
|
||||
androidTestCompile('com.android.support.test:rules:+') {
|
||||
exclude group: 'com.android.support', module: 'support-annotations'
|
||||
}
|
||||
androidTestCompile 'junit:junit:4.12'
|
||||
androidTestCompile 'com.squareup.okhttp3:mockwebserver:3.9.0'
|
||||
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile 'com.squareup.okhttp3:mockwebserver:3.9.0'
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
**************************************************************************************************/
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.mikepenz.aboutLibraries)
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.compose.compiler)
|
||||
alias(libs.plugins.hilt)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.ksp)
|
||||
}
|
||||
|
||||
// Android configuration
|
||||
android {
|
||||
compileSdk = 35
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "at.bitfire.davdroid"
|
||||
|
||||
versionCode = 404030200
|
||||
versionName = "4.4.3.2"
|
||||
|
||||
setProperty("archivesBaseName", "davx5-ose-$versionName")
|
||||
|
||||
minSdk = 24 // Android 7.0
|
||||
targetSdk = 35 // Android 15
|
||||
|
||||
testInstrumentationRunner = "at.bitfire.davdroid.HiltTestRunner"
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
// required for
|
||||
// - dnsjava 3.x: java.nio.file.Path
|
||||
// - ical4android: time API
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
compose = true
|
||||
}
|
||||
|
||||
// Java namespace for our classes (not to be confused with Android package ID)
|
||||
namespace = "at.bitfire.davdroid"
|
||||
|
||||
flavorDimensions += "distribution"
|
||||
productFlavors {
|
||||
create("ose") {
|
||||
dimension = "distribution"
|
||||
versionNameSuffix = "-ose"
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
getByName("androidTest") {
|
||||
assets.srcDir("$projectDir/schemas")
|
||||
}
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
create("bitfire") {
|
||||
storeFile = file(System.getenv("ANDROID_KEYSTORE") ?: "/dev/null")
|
||||
storePassword = System.getenv("ANDROID_KEYSTORE_PASSWORD")
|
||||
keyAlias = System.getenv("ANDROID_KEY_ALIAS")
|
||||
keyPassword = System.getenv("ANDROID_KEY_PASSWORD")
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
getByName("release") {
|
||||
isMinifyEnabled = true
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules-release.pro")
|
||||
|
||||
isShrinkResources = true
|
||||
|
||||
signingConfig = signingConfigs.findByName("bitfire")
|
||||
}
|
||||
getByName("debug") {
|
||||
applicationIdSuffix = ".debug"
|
||||
}
|
||||
}
|
||||
|
||||
lint {
|
||||
disable += arrayOf("GoogleAppIndexingWarning", "ImpliedQuantity", "MissingQuantity", "MissingTranslation", "ExtraTranslation", "RtlEnabled", "RtlHardcoded", "Typos", "NullSafeMutableLiveData")
|
||||
}
|
||||
|
||||
packaging {
|
||||
resources {
|
||||
excludes += arrayOf("META-INF/*.md")
|
||||
}
|
||||
}
|
||||
|
||||
androidResources {
|
||||
generateLocaleConfig = true
|
||||
}
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
testOptions {
|
||||
managedDevices {
|
||||
localDevices {
|
||||
create("virtual") {
|
||||
device = "Pixel 3"
|
||||
apiLevel = 34
|
||||
systemImageSource = "aosp-atd"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ksp {
|
||||
arg("room.schemaLocation", "$projectDir/schemas")
|
||||
}
|
||||
|
||||
aboutLibraries {
|
||||
excludeFields = arrayOf("generated")
|
||||
}
|
||||
|
||||
configurations {
|
||||
configureEach {
|
||||
// exclude modules which are in conflict with system libraries
|
||||
exclude(module="commons-logging")
|
||||
exclude(group="org.json", module="json")
|
||||
|
||||
// Groovy requires SDK 26+, and it's not required, so exclude it
|
||||
exclude(group="org.codehaus.groovy")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// core
|
||||
implementation(libs.kotlin.stdlib)
|
||||
implementation(libs.kotlinx.coroutines)
|
||||
coreLibraryDesugaring(libs.android.desugaring)
|
||||
|
||||
// Hilt
|
||||
implementation(libs.hilt.android.base)
|
||||
ksp(libs.androidx.hilt.compiler)
|
||||
ksp(libs.hilt.android.compiler)
|
||||
|
||||
// support libs
|
||||
implementation(libs.androidx.activityCompose)
|
||||
implementation(libs.androidx.appcompat)
|
||||
implementation(libs.androidx.browser)
|
||||
implementation(libs.androidx.core)
|
||||
implementation(libs.androidx.hilt.navigation.compose)
|
||||
implementation(libs.androidx.hilt.work)
|
||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
||||
implementation(libs.androidx.lifecycle.viewmodel.base)
|
||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||
implementation(libs.androidx.paging)
|
||||
implementation(libs.androidx.paging.compose)
|
||||
implementation(libs.androidx.preference)
|
||||
implementation(libs.androidx.security)
|
||||
implementation(libs.androidx.work.base)
|
||||
|
||||
// Jetpack Compose
|
||||
implementation(libs.compose.accompanist.permissions)
|
||||
implementation(platform(libs.compose.bom))
|
||||
implementation(libs.compose.material3)
|
||||
implementation(libs.compose.materialIconsExtended)
|
||||
implementation(libs.compose.runtime.livedata)
|
||||
debugImplementation(libs.compose.ui.tooling)
|
||||
implementation(libs.compose.ui.toolingPreview)
|
||||
|
||||
// Glance Widgets
|
||||
implementation(libs.glance.base)
|
||||
implementation(libs.glance.material)
|
||||
|
||||
// Jetpack Room
|
||||
implementation(libs.room.runtime)
|
||||
implementation(libs.room.base)
|
||||
implementation(libs.room.paging)
|
||||
ksp(libs.room.compiler)
|
||||
|
||||
// own libraries
|
||||
implementation(libs.bitfire.cert4android)
|
||||
implementation(libs.bitfire.dav4jvm) {
|
||||
exclude(group="junit")
|
||||
}
|
||||
implementation(libs.bitfire.ical4android)
|
||||
implementation(libs.bitfire.vcard4android)
|
||||
|
||||
// third-party libs
|
||||
@Suppress("RedundantSuppression")
|
||||
implementation(libs.dnsjava)
|
||||
implementation(libs.guava)
|
||||
implementation(libs.mikepenz.aboutLibraries)
|
||||
implementation(libs.nsk90.kstatemachine)
|
||||
implementation(libs.okhttp.base)
|
||||
implementation(libs.okhttp.brotli)
|
||||
implementation(libs.okhttp.logging)
|
||||
implementation(libs.openid.appauth)
|
||||
implementation(libs.unifiedpush)
|
||||
|
||||
// for tests
|
||||
androidTestImplementation(libs.androidx.arch.core.testing)
|
||||
androidTestImplementation(libs.androidx.test.core)
|
||||
androidTestImplementation(libs.androidx.test.junit)
|
||||
androidTestImplementation(libs.androidx.test.rules)
|
||||
androidTestImplementation(libs.androidx.test.runner)
|
||||
androidTestImplementation(libs.androidx.work.testing)
|
||||
androidTestImplementation(libs.hilt.android.testing)
|
||||
androidTestImplementation(libs.junit)
|
||||
androidTestImplementation(libs.mockk.android)
|
||||
androidTestImplementation(libs.okhttp.mockwebserver)
|
||||
androidTestImplementation(libs.room.testing)
|
||||
|
||||
testImplementation(libs.junit)
|
||||
testImplementation(libs.mockk)
|
||||
testImplementation(libs.okhttp.mockwebserver)
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
|
||||
# R8 usage for DAVx⁵:
|
||||
# shrinking yes (only in release builds)
|
||||
# optimization yes (on by R8 defaults)
|
||||
# full-mode no (see gradle.properties)
|
||||
# obfuscation no (open-source)
|
||||
|
||||
-dontobfuscate
|
||||
-printusage build/reports/r8-usage.txt
|
||||
|
||||
# ez-vcard: keep all vCard properties/parameters (used via reflection)
|
||||
-keep class ezvcard.io.scribe.** { *; }
|
||||
-keep class ezvcard.property.** { *; }
|
||||
-keep class ezvcard.parameter.** { *; }
|
||||
|
||||
# ical4j: keep all iCalendar properties/parameters (used via reflection)
|
||||
-keep class net.fortuna.ical4j.** { *; }
|
||||
|
||||
# XmlPullParser
|
||||
-keep class org.xmlpull.** { *; }
|
||||
|
||||
# DAVx⁵ + libs
|
||||
-keep class at.bitfire.** { *; } # all DAVx⁵ code is required
|
||||
|
||||
# AGP 8.2 and 8.3 seem to remove this class, but ezvcard.io uses it. See https://github.com/bitfireAT/davx5/issues/499
|
||||
-keep class javax.xml.namespace.QName { *; }
|
||||
|
||||
# we use enum classes (https://www.guardsquare.com/en/products/proguard/manual/examples#enumerations)
|
||||
-keepclassmembers,allowoptimization enum * {
|
||||
public static **[] values();
|
||||
public static ** valueOf(java.lang.String);
|
||||
}
|
||||
|
||||
# Additional rules which are now required since missing classes can't be ignored in R8 anymore.
|
||||
# [https://developer.android.com/build/releases/past-releases/agp-7-0-0-release-notes#r8-missing-class-warning]
|
||||
-dontwarn com.android.org.conscrypt.SSLParametersImpl
|
||||
-dontwarn com.github.erosb.jsonsKema.** # ical4j
|
||||
-dontwarn com.google.errorprone.annotations.**
|
||||
-dontwarn com.sun.jna.** # dnsjava
|
||||
-dontwarn groovy.**
|
||||
-dontwarn java.beans.Transient
|
||||
-dontwarn javax.cache.** # ical4j
|
||||
-dontwarn javax.naming.NamingException # dnsjava
|
||||
-dontwarn javax.naming.directory.** # dnsjava
|
||||
-dontwarn junit.textui.TestRunner
|
||||
-dontwarn lombok.** # dnsjava
|
||||
-dontwarn org.apache.harmony.xnet.provider.jsse.SSLParametersImpl
|
||||
-dontwarn org.bouncycastle.jsse.**
|
||||
-dontwarn org.codehaus.groovy.**
|
||||
-dontwarn org.joda.**
|
||||
-dontwarn org.jparsec.** # ical4j
|
||||
-dontwarn org.json.*
|
||||
-dontwarn org.jsoup.**
|
||||
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
|
||||
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
|
||||
-dontwarn org.openjsse.net.ssl.OpenJSSE
|
||||
-dontwarn org.xbill.DNS.spi.DnsjavaInetAddressResolverProvider # dnsjava
|
||||
-dontwarn org.xmlpull.**
|
||||
-dontwarn sun.net.spi.nameservice.NameService
|
||||
-dontwarn sun.net.spi.nameservice.NameServiceDescriptor
|
||||
42
app/proguard-rules.txt
Normal file
42
app/proguard-rules.txt
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
# ProGuard usage for DAVdroid:
|
||||
# shrinking yes (main reason for using ProGuard)
|
||||
# optimization yes
|
||||
# obfuscation no (DAVdroid is open-source)
|
||||
# preverification no
|
||||
|
||||
-dontobfuscate
|
||||
|
||||
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
|
||||
-optimizationpasses 5
|
||||
-allowaccessmodification
|
||||
-dontpreverify
|
||||
|
||||
# Kotlin
|
||||
-dontwarn kotlin.**
|
||||
|
||||
# ez-vcard
|
||||
-dontwarn ezvcard.io.json.** # JSON serializer (for jCards) not used
|
||||
-dontwarn freemarker.** # freemarker templating library (for creating hCards) not used
|
||||
-dontwarn org.jsoup.** # jsoup library (for hCard parsing) not used
|
||||
-keep class ezvcard.property.** { *; } # keep all VCard properties (created at runtime)
|
||||
|
||||
# ical4j: ignore unused dynamic libraries
|
||||
-dontwarn aQute.**
|
||||
-dontwarn groovy.** # Groovy-based ContentBuilder not used
|
||||
-dontwarn net.fortuna.ical4j.model.**
|
||||
-dontwarn org.codehaus.groovy.**
|
||||
-dontwarn org.apache.log4j.** # ignore warnings from log4j dependency
|
||||
-keep class net.fortuna.ical4j.** { *; } # keep all model classes (properties/factories, created at runtime)
|
||||
-keep class org.threeten.bp.** { *; } # keep ThreeTen (for time zone processing)
|
||||
|
||||
# okhttp
|
||||
-dontwarn okio.**
|
||||
-dontwarn javax.annotation.Nullable
|
||||
-dontwarn javax.annotation.ParametersAreNonnullByDefault
|
||||
|
||||
# dnsjava
|
||||
-dontwarn sun.net.spi.nameservice.** # not available on Android
|
||||
|
||||
# DAVdroid + libs
|
||||
-keep class at.bitfire.** { *; } # all DAVdroid code is required
|
||||
@@ -1,398 +0,0 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 10,
|
||||
"identityHash": "6fcabe50cbd00a4215dbe536a565dd2a",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "service",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `accountName` TEXT NOT NULL, `type` TEXT NOT NULL, `principal` TEXT)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountName",
|
||||
"columnName": "accountName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "principal",
|
||||
"columnName": "principal",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_service_accountName_type",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"accountName",
|
||||
"type"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_service_accountName_type` ON `${TABLE_NAME}` (`accountName`, `type`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "homeset",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `serviceId` INTEGER NOT NULL, `personal` INTEGER NOT NULL, `url` TEXT NOT NULL, `privBind` INTEGER NOT NULL, `displayName` TEXT, FOREIGN KEY(`serviceId`) REFERENCES `service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceId",
|
||||
"columnName": "serviceId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "personal",
|
||||
"columnName": "personal",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privBind",
|
||||
"columnName": "privBind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_homeset_serviceId_url",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"serviceId",
|
||||
"url"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_homeset_serviceId_url` ON `${TABLE_NAME}` (`serviceId`, `url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "service",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"serviceId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "collection",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `serviceId` INTEGER NOT NULL, `homeSetId` INTEGER, `type` TEXT NOT NULL, `url` TEXT NOT NULL, `privWriteContent` INTEGER NOT NULL, `privUnbind` INTEGER NOT NULL, `forceReadOnly` INTEGER NOT NULL, `displayName` TEXT, `description` TEXT, `owner` TEXT, `color` INTEGER, `timezone` TEXT, `supportsVEVENT` INTEGER, `supportsVTODO` INTEGER, `supportsVJOURNAL` INTEGER, `source` TEXT, `sync` INTEGER NOT NULL, FOREIGN KEY(`serviceId`) REFERENCES `service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`homeSetId`) REFERENCES `homeset`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceId",
|
||||
"columnName": "serviceId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "homeSetId",
|
||||
"columnName": "homeSetId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privWriteContent",
|
||||
"columnName": "privWriteContent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privUnbind",
|
||||
"columnName": "privUnbind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "forceReadOnly",
|
||||
"columnName": "forceReadOnly",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "description",
|
||||
"columnName": "description",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "owner",
|
||||
"columnName": "owner",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "color",
|
||||
"columnName": "color",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "timezone",
|
||||
"columnName": "timezone",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVEVENT",
|
||||
"columnName": "supportsVEVENT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVTODO",
|
||||
"columnName": "supportsVTODO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVJOURNAL",
|
||||
"columnName": "supportsVJOURNAL",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "source",
|
||||
"columnName": "source",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "sync",
|
||||
"columnName": "sync",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_collection_serviceId_type",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"serviceId",
|
||||
"type"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_serviceId_type` ON `${TABLE_NAME}` (`serviceId`, `type`)"
|
||||
},
|
||||
{
|
||||
"name": "index_collection_homeSetId_type",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"homeSetId",
|
||||
"type"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_homeSetId_type` ON `${TABLE_NAME}` (`homeSetId`, `type`)"
|
||||
},
|
||||
{
|
||||
"name": "index_collection_url",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"url"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_url` ON `${TABLE_NAME}` (`url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "service",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"serviceId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "homeset",
|
||||
"onDelete": "SET NULL",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"homeSetId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "syncstats",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `collectionId` INTEGER NOT NULL, `authority` TEXT NOT NULL, `lastSync` INTEGER NOT NULL, FOREIGN KEY(`collectionId`) REFERENCES `collection`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "collectionId",
|
||||
"columnName": "collectionId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "authority",
|
||||
"columnName": "authority",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastSync",
|
||||
"columnName": "lastSync",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_syncstats_collectionId_authority",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"collectionId",
|
||||
"authority"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_syncstats_collectionId_authority` ON `${TABLE_NAME}` (`collectionId`, `authority`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "collection",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"collectionId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "webdav_mount",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6fcabe50cbd00a4215dbe536a565dd2a')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,536 +0,0 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 11,
|
||||
"identityHash": "223aa7f0fd53730921ca212a663585d8",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "service",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `accountName` TEXT NOT NULL, `type` TEXT NOT NULL, `principal` TEXT)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountName",
|
||||
"columnName": "accountName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "principal",
|
||||
"columnName": "principal",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_service_accountName_type",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"accountName",
|
||||
"type"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_service_accountName_type` ON `${TABLE_NAME}` (`accountName`, `type`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "homeset",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `serviceId` INTEGER NOT NULL, `personal` INTEGER NOT NULL, `url` TEXT NOT NULL, `privBind` INTEGER NOT NULL, `displayName` TEXT, FOREIGN KEY(`serviceId`) REFERENCES `service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceId",
|
||||
"columnName": "serviceId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "personal",
|
||||
"columnName": "personal",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privBind",
|
||||
"columnName": "privBind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_homeset_serviceId_url",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"serviceId",
|
||||
"url"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_homeset_serviceId_url` ON `${TABLE_NAME}` (`serviceId`, `url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "service",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"serviceId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "collection",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `serviceId` INTEGER NOT NULL, `homeSetId` INTEGER, `type` TEXT NOT NULL, `url` TEXT NOT NULL, `privWriteContent` INTEGER NOT NULL, `privUnbind` INTEGER NOT NULL, `forceReadOnly` INTEGER NOT NULL, `displayName` TEXT, `description` TEXT, `owner` TEXT, `color` INTEGER, `timezone` TEXT, `supportsVEVENT` INTEGER, `supportsVTODO` INTEGER, `supportsVJOURNAL` INTEGER, `source` TEXT, `sync` INTEGER NOT NULL, FOREIGN KEY(`serviceId`) REFERENCES `service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`homeSetId`) REFERENCES `homeset`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceId",
|
||||
"columnName": "serviceId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "homeSetId",
|
||||
"columnName": "homeSetId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privWriteContent",
|
||||
"columnName": "privWriteContent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privUnbind",
|
||||
"columnName": "privUnbind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "forceReadOnly",
|
||||
"columnName": "forceReadOnly",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "description",
|
||||
"columnName": "description",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "owner",
|
||||
"columnName": "owner",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "color",
|
||||
"columnName": "color",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "timezone",
|
||||
"columnName": "timezone",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVEVENT",
|
||||
"columnName": "supportsVEVENT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVTODO",
|
||||
"columnName": "supportsVTODO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVJOURNAL",
|
||||
"columnName": "supportsVJOURNAL",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "source",
|
||||
"columnName": "source",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "sync",
|
||||
"columnName": "sync",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_collection_serviceId_type",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"serviceId",
|
||||
"type"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_serviceId_type` ON `${TABLE_NAME}` (`serviceId`, `type`)"
|
||||
},
|
||||
{
|
||||
"name": "index_collection_homeSetId_type",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"homeSetId",
|
||||
"type"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_homeSetId_type` ON `${TABLE_NAME}` (`homeSetId`, `type`)"
|
||||
},
|
||||
{
|
||||
"name": "index_collection_url",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"url"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_url` ON `${TABLE_NAME}` (`url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "service",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"serviceId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "homeset",
|
||||
"onDelete": "SET NULL",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"homeSetId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "syncstats",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `collectionId` INTEGER NOT NULL, `authority` TEXT NOT NULL, `lastSync` INTEGER NOT NULL, FOREIGN KEY(`collectionId`) REFERENCES `collection`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "collectionId",
|
||||
"columnName": "collectionId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "authority",
|
||||
"columnName": "authority",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastSync",
|
||||
"columnName": "lastSync",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_syncstats_collectionId_authority",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"collectionId",
|
||||
"authority"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_syncstats_collectionId_authority` ON `${TABLE_NAME}` (`collectionId`, `authority`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "collection",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"collectionId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "webdav_document",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `mountId` INTEGER NOT NULL, `parentId` INTEGER, `name` TEXT NOT NULL, `isDirectory` INTEGER NOT NULL, `displayName` TEXT, `mimeType` TEXT, `eTag` TEXT, `lastModified` INTEGER, `size` INTEGER, `mayBind` INTEGER, `mayUnbind` INTEGER, `mayWriteContent` INTEGER, `quotaAvailable` INTEGER, `quotaUsed` INTEGER, FOREIGN KEY(`mountId`) REFERENCES `webdav_mount`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`parentId`) REFERENCES `webdav_document`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mountId",
|
||||
"columnName": "mountId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentId",
|
||||
"columnName": "parentId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDirectory",
|
||||
"columnName": "isDirectory",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mimeType",
|
||||
"columnName": "mimeType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "eTag",
|
||||
"columnName": "eTag",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastModified",
|
||||
"columnName": "lastModified",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mayBind",
|
||||
"columnName": "mayBind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mayUnbind",
|
||||
"columnName": "mayUnbind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mayWriteContent",
|
||||
"columnName": "mayWriteContent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "quotaAvailable",
|
||||
"columnName": "quotaAvailable",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "quotaUsed",
|
||||
"columnName": "quotaUsed",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_webdav_document_mountId_parentId_name",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"mountId",
|
||||
"parentId",
|
||||
"name"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_webdav_document_mountId_parentId_name` ON `${TABLE_NAME}` (`mountId`, `parentId`, `name`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "webdav_mount",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"mountId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "webdav_document",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"parentId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "webdav_mount",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '223aa7f0fd53730921ca212a663585d8')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,615 +0,0 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 12,
|
||||
"identityHash": "67fafceecee2d97cac6a62d46fa2c3e2",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "service",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `accountName` TEXT NOT NULL, `type` TEXT NOT NULL, `principal` TEXT)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountName",
|
||||
"columnName": "accountName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "principal",
|
||||
"columnName": "principal",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_service_accountName_type",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"accountName",
|
||||
"type"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_service_accountName_type` ON `${TABLE_NAME}` (`accountName`, `type`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "homeset",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `serviceId` INTEGER NOT NULL, `personal` INTEGER NOT NULL, `url` TEXT NOT NULL, `privBind` INTEGER NOT NULL, `displayName` TEXT, FOREIGN KEY(`serviceId`) REFERENCES `service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceId",
|
||||
"columnName": "serviceId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "personal",
|
||||
"columnName": "personal",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privBind",
|
||||
"columnName": "privBind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_homeset_serviceId_url",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"serviceId",
|
||||
"url"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_homeset_serviceId_url` ON `${TABLE_NAME}` (`serviceId`, `url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "service",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"serviceId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "collection",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `serviceId` INTEGER NOT NULL, `homeSetId` INTEGER, `ownerId` INTEGER, `type` TEXT NOT NULL, `url` TEXT NOT NULL, `privWriteContent` INTEGER NOT NULL, `privUnbind` INTEGER NOT NULL, `forceReadOnly` INTEGER NOT NULL, `displayName` TEXT, `description` TEXT, `color` INTEGER, `timezone` TEXT, `supportsVEVENT` INTEGER, `supportsVTODO` INTEGER, `supportsVJOURNAL` INTEGER, `source` TEXT, `sync` INTEGER NOT NULL, FOREIGN KEY(`serviceId`) REFERENCES `service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`homeSetId`) REFERENCES `homeset`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL , FOREIGN KEY(`ownerId`) REFERENCES `principal`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceId",
|
||||
"columnName": "serviceId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "homeSetId",
|
||||
"columnName": "homeSetId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "ownerId",
|
||||
"columnName": "ownerId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privWriteContent",
|
||||
"columnName": "privWriteContent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privUnbind",
|
||||
"columnName": "privUnbind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "forceReadOnly",
|
||||
"columnName": "forceReadOnly",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "description",
|
||||
"columnName": "description",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "color",
|
||||
"columnName": "color",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "timezone",
|
||||
"columnName": "timezone",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVEVENT",
|
||||
"columnName": "supportsVEVENT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVTODO",
|
||||
"columnName": "supportsVTODO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVJOURNAL",
|
||||
"columnName": "supportsVJOURNAL",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "source",
|
||||
"columnName": "source",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "sync",
|
||||
"columnName": "sync",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_collection_serviceId_type",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"serviceId",
|
||||
"type"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_serviceId_type` ON `${TABLE_NAME}` (`serviceId`, `type`)"
|
||||
},
|
||||
{
|
||||
"name": "index_collection_homeSetId_type",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"homeSetId",
|
||||
"type"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_homeSetId_type` ON `${TABLE_NAME}` (`homeSetId`, `type`)"
|
||||
},
|
||||
{
|
||||
"name": "index_collection_url",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"url"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_url` ON `${TABLE_NAME}` (`url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "service",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"serviceId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "homeset",
|
||||
"onDelete": "SET NULL",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"homeSetId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "principal",
|
||||
"onDelete": "SET NULL",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"ownerId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "principal",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `serviceId` INTEGER NOT NULL, `url` TEXT NOT NULL, `displayName` TEXT, FOREIGN KEY(`serviceId`) REFERENCES `service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceId",
|
||||
"columnName": "serviceId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_principal_serviceId_url",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"serviceId",
|
||||
"url"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_principal_serviceId_url` ON `${TABLE_NAME}` (`serviceId`, `url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "service",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"serviceId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "syncstats",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `collectionId` INTEGER NOT NULL, `authority` TEXT NOT NULL, `lastSync` INTEGER NOT NULL, FOREIGN KEY(`collectionId`) REFERENCES `collection`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "collectionId",
|
||||
"columnName": "collectionId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "authority",
|
||||
"columnName": "authority",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastSync",
|
||||
"columnName": "lastSync",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_syncstats_collectionId_authority",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"collectionId",
|
||||
"authority"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_syncstats_collectionId_authority` ON `${TABLE_NAME}` (`collectionId`, `authority`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "collection",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"collectionId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "webdav_document",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `mountId` INTEGER NOT NULL, `parentId` INTEGER, `name` TEXT NOT NULL, `isDirectory` INTEGER NOT NULL, `displayName` TEXT, `mimeType` TEXT, `eTag` TEXT, `lastModified` INTEGER, `size` INTEGER, `mayBind` INTEGER, `mayUnbind` INTEGER, `mayWriteContent` INTEGER, `quotaAvailable` INTEGER, `quotaUsed` INTEGER, FOREIGN KEY(`mountId`) REFERENCES `webdav_mount`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`parentId`) REFERENCES `webdav_document`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mountId",
|
||||
"columnName": "mountId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentId",
|
||||
"columnName": "parentId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDirectory",
|
||||
"columnName": "isDirectory",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mimeType",
|
||||
"columnName": "mimeType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "eTag",
|
||||
"columnName": "eTag",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastModified",
|
||||
"columnName": "lastModified",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mayBind",
|
||||
"columnName": "mayBind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mayUnbind",
|
||||
"columnName": "mayUnbind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mayWriteContent",
|
||||
"columnName": "mayWriteContent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "quotaAvailable",
|
||||
"columnName": "quotaAvailable",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "quotaUsed",
|
||||
"columnName": "quotaUsed",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_webdav_document_mountId_parentId_name",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"mountId",
|
||||
"parentId",
|
||||
"name"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_webdav_document_mountId_parentId_name` ON `${TABLE_NAME}` (`mountId`, `parentId`, `name`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "webdav_mount",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"mountId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "webdav_document",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"parentId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "webdav_mount",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '67fafceecee2d97cac6a62d46fa2c3e2')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,640 +0,0 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 13,
|
||||
"identityHash": "0a6a9705ff471acd766ab96e3edf8ac3",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "service",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `accountName` TEXT NOT NULL, `type` TEXT NOT NULL, `principal` TEXT)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountName",
|
||||
"columnName": "accountName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "principal",
|
||||
"columnName": "principal",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_service_accountName_type",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"accountName",
|
||||
"type"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_service_accountName_type` ON `${TABLE_NAME}` (`accountName`, `type`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "homeset",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `serviceId` INTEGER NOT NULL, `personal` INTEGER NOT NULL, `url` TEXT NOT NULL, `privBind` INTEGER NOT NULL, `displayName` TEXT, FOREIGN KEY(`serviceId`) REFERENCES `service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceId",
|
||||
"columnName": "serviceId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "personal",
|
||||
"columnName": "personal",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privBind",
|
||||
"columnName": "privBind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_homeset_serviceId_url",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"serviceId",
|
||||
"url"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_homeset_serviceId_url` ON `${TABLE_NAME}` (`serviceId`, `url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "service",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"serviceId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "collection",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `serviceId` INTEGER NOT NULL, `homeSetId` INTEGER, `ownerId` INTEGER, `type` TEXT NOT NULL, `url` TEXT NOT NULL, `privWriteContent` INTEGER NOT NULL, `privUnbind` INTEGER NOT NULL, `forceReadOnly` INTEGER NOT NULL, `displayName` TEXT, `description` TEXT, `color` INTEGER, `timezone` TEXT, `supportsVEVENT` INTEGER, `supportsVTODO` INTEGER, `supportsVJOURNAL` INTEGER, `source` TEXT, `sync` INTEGER NOT NULL, `pushTopic` TEXT, `supportsWebPush` INTEGER NOT NULL DEFAULT 0, `pushSubscription` TEXT, `pushSubscriptionCreated` INTEGER, FOREIGN KEY(`serviceId`) REFERENCES `service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`homeSetId`) REFERENCES `homeset`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL , FOREIGN KEY(`ownerId`) REFERENCES `principal`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceId",
|
||||
"columnName": "serviceId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "homeSetId",
|
||||
"columnName": "homeSetId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "ownerId",
|
||||
"columnName": "ownerId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privWriteContent",
|
||||
"columnName": "privWriteContent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privUnbind",
|
||||
"columnName": "privUnbind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "forceReadOnly",
|
||||
"columnName": "forceReadOnly",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "description",
|
||||
"columnName": "description",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "color",
|
||||
"columnName": "color",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "timezone",
|
||||
"columnName": "timezone",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVEVENT",
|
||||
"columnName": "supportsVEVENT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVTODO",
|
||||
"columnName": "supportsVTODO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVJOURNAL",
|
||||
"columnName": "supportsVJOURNAL",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "source",
|
||||
"columnName": "source",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "sync",
|
||||
"columnName": "sync",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushTopic",
|
||||
"columnName": "pushTopic",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsWebPush",
|
||||
"columnName": "supportsWebPush",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushSubscription",
|
||||
"columnName": "pushSubscription",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushSubscriptionCreated",
|
||||
"columnName": "pushSubscriptionCreated",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_collection_serviceId_type",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"serviceId",
|
||||
"type"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_serviceId_type` ON `${TABLE_NAME}` (`serviceId`, `type`)"
|
||||
},
|
||||
{
|
||||
"name": "index_collection_homeSetId_type",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"homeSetId",
|
||||
"type"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_homeSetId_type` ON `${TABLE_NAME}` (`homeSetId`, `type`)"
|
||||
},
|
||||
{
|
||||
"name": "index_collection_url",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"url"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_url` ON `${TABLE_NAME}` (`url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "service",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"serviceId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "homeset",
|
||||
"onDelete": "SET NULL",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"homeSetId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "principal",
|
||||
"onDelete": "SET NULL",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"ownerId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "principal",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `serviceId` INTEGER NOT NULL, `url` TEXT NOT NULL, `displayName` TEXT, FOREIGN KEY(`serviceId`) REFERENCES `service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceId",
|
||||
"columnName": "serviceId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_principal_serviceId_url",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"serviceId",
|
||||
"url"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_principal_serviceId_url` ON `${TABLE_NAME}` (`serviceId`, `url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "service",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"serviceId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "syncstats",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `collectionId` INTEGER NOT NULL, `authority` TEXT NOT NULL, `lastSync` INTEGER NOT NULL, FOREIGN KEY(`collectionId`) REFERENCES `collection`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "collectionId",
|
||||
"columnName": "collectionId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "authority",
|
||||
"columnName": "authority",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastSync",
|
||||
"columnName": "lastSync",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_syncstats_collectionId_authority",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"collectionId",
|
||||
"authority"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_syncstats_collectionId_authority` ON `${TABLE_NAME}` (`collectionId`, `authority`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "collection",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"collectionId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "webdav_document",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `mountId` INTEGER NOT NULL, `parentId` INTEGER, `name` TEXT NOT NULL, `isDirectory` INTEGER NOT NULL, `displayName` TEXT, `mimeType` TEXT, `eTag` TEXT, `lastModified` INTEGER, `size` INTEGER, `mayBind` INTEGER, `mayUnbind` INTEGER, `mayWriteContent` INTEGER, `quotaAvailable` INTEGER, `quotaUsed` INTEGER, FOREIGN KEY(`mountId`) REFERENCES `webdav_mount`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`parentId`) REFERENCES `webdav_document`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mountId",
|
||||
"columnName": "mountId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentId",
|
||||
"columnName": "parentId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDirectory",
|
||||
"columnName": "isDirectory",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mimeType",
|
||||
"columnName": "mimeType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "eTag",
|
||||
"columnName": "eTag",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastModified",
|
||||
"columnName": "lastModified",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mayBind",
|
||||
"columnName": "mayBind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mayUnbind",
|
||||
"columnName": "mayUnbind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mayWriteContent",
|
||||
"columnName": "mayWriteContent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "quotaAvailable",
|
||||
"columnName": "quotaAvailable",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "quotaUsed",
|
||||
"columnName": "quotaUsed",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_webdav_document_mountId_parentId_name",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"mountId",
|
||||
"parentId",
|
||||
"name"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_webdav_document_mountId_parentId_name` ON `${TABLE_NAME}` (`mountId`, `parentId`, `name`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "webdav_mount",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"mountId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "webdav_document",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"parentId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "webdav_mount",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0a6a9705ff471acd766ab96e3edf8ac3')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,669 +0,0 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 14,
|
||||
"identityHash": "9a0eb47f27473eab254db568081a4585",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "service",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `accountName` TEXT NOT NULL, `type` TEXT NOT NULL, `principal` TEXT)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountName",
|
||||
"columnName": "accountName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "principal",
|
||||
"columnName": "principal",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_service_accountName_type",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"accountName",
|
||||
"type"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_service_accountName_type` ON `${TABLE_NAME}` (`accountName`, `type`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "homeset",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `serviceId` INTEGER NOT NULL, `personal` INTEGER NOT NULL, `url` TEXT NOT NULL, `privBind` INTEGER NOT NULL, `displayName` TEXT, FOREIGN KEY(`serviceId`) REFERENCES `service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceId",
|
||||
"columnName": "serviceId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "personal",
|
||||
"columnName": "personal",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privBind",
|
||||
"columnName": "privBind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_homeset_serviceId_url",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"serviceId",
|
||||
"url"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_homeset_serviceId_url` ON `${TABLE_NAME}` (`serviceId`, `url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "service",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"serviceId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "collection",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `serviceId` INTEGER NOT NULL, `homeSetId` INTEGER, `ownerId` INTEGER, `type` TEXT NOT NULL, `url` TEXT NOT NULL, `privWriteContent` INTEGER NOT NULL, `privUnbind` INTEGER NOT NULL, `forceReadOnly` INTEGER NOT NULL, `displayName` TEXT, `description` TEXT, `color` INTEGER, `timezone` TEXT, `supportsVEVENT` INTEGER, `supportsVTODO` INTEGER, `supportsVJOURNAL` INTEGER, `source` TEXT, `sync` INTEGER NOT NULL, `pushTopic` TEXT, `supportsWebPush` INTEGER NOT NULL DEFAULT 0, `pushSubscription` TEXT, `pushSubscriptionCreated` INTEGER, FOREIGN KEY(`serviceId`) REFERENCES `service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`homeSetId`) REFERENCES `homeset`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL , FOREIGN KEY(`ownerId`) REFERENCES `principal`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceId",
|
||||
"columnName": "serviceId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "homeSetId",
|
||||
"columnName": "homeSetId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "ownerId",
|
||||
"columnName": "ownerId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privWriteContent",
|
||||
"columnName": "privWriteContent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privUnbind",
|
||||
"columnName": "privUnbind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "forceReadOnly",
|
||||
"columnName": "forceReadOnly",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "description",
|
||||
"columnName": "description",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "color",
|
||||
"columnName": "color",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "timezone",
|
||||
"columnName": "timezone",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVEVENT",
|
||||
"columnName": "supportsVEVENT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVTODO",
|
||||
"columnName": "supportsVTODO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVJOURNAL",
|
||||
"columnName": "supportsVJOURNAL",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "source",
|
||||
"columnName": "source",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "sync",
|
||||
"columnName": "sync",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushTopic",
|
||||
"columnName": "pushTopic",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsWebPush",
|
||||
"columnName": "supportsWebPush",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushSubscription",
|
||||
"columnName": "pushSubscription",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushSubscriptionCreated",
|
||||
"columnName": "pushSubscriptionCreated",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_collection_serviceId_type",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"serviceId",
|
||||
"type"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_serviceId_type` ON `${TABLE_NAME}` (`serviceId`, `type`)"
|
||||
},
|
||||
{
|
||||
"name": "index_collection_homeSetId_type",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"homeSetId",
|
||||
"type"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_homeSetId_type` ON `${TABLE_NAME}` (`homeSetId`, `type`)"
|
||||
},
|
||||
{
|
||||
"name": "index_collection_ownerId_type",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"ownerId",
|
||||
"type"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_ownerId_type` ON `${TABLE_NAME}` (`ownerId`, `type`)"
|
||||
},
|
||||
{
|
||||
"name": "index_collection_pushTopic_type",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"pushTopic",
|
||||
"type"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_pushTopic_type` ON `${TABLE_NAME}` (`pushTopic`, `type`)"
|
||||
},
|
||||
{
|
||||
"name": "index_collection_url",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"url"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_url` ON `${TABLE_NAME}` (`url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "service",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"serviceId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "homeset",
|
||||
"onDelete": "SET NULL",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"homeSetId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "principal",
|
||||
"onDelete": "SET NULL",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"ownerId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "principal",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `serviceId` INTEGER NOT NULL, `url` TEXT NOT NULL, `displayName` TEXT, FOREIGN KEY(`serviceId`) REFERENCES `service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceId",
|
||||
"columnName": "serviceId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_principal_serviceId_url",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"serviceId",
|
||||
"url"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_principal_serviceId_url` ON `${TABLE_NAME}` (`serviceId`, `url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "service",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"serviceId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "syncstats",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `collectionId` INTEGER NOT NULL, `authority` TEXT NOT NULL, `lastSync` INTEGER NOT NULL, FOREIGN KEY(`collectionId`) REFERENCES `collection`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "collectionId",
|
||||
"columnName": "collectionId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "authority",
|
||||
"columnName": "authority",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastSync",
|
||||
"columnName": "lastSync",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_syncstats_collectionId_authority",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"collectionId",
|
||||
"authority"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_syncstats_collectionId_authority` ON `${TABLE_NAME}` (`collectionId`, `authority`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "collection",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"collectionId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "webdav_document",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `mountId` INTEGER NOT NULL, `parentId` INTEGER, `name` TEXT NOT NULL, `isDirectory` INTEGER NOT NULL, `displayName` TEXT, `mimeType` TEXT, `eTag` TEXT, `lastModified` INTEGER, `size` INTEGER, `mayBind` INTEGER, `mayUnbind` INTEGER, `mayWriteContent` INTEGER, `quotaAvailable` INTEGER, `quotaUsed` INTEGER, FOREIGN KEY(`mountId`) REFERENCES `webdav_mount`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`parentId`) REFERENCES `webdav_document`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mountId",
|
||||
"columnName": "mountId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentId",
|
||||
"columnName": "parentId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDirectory",
|
||||
"columnName": "isDirectory",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mimeType",
|
||||
"columnName": "mimeType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "eTag",
|
||||
"columnName": "eTag",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastModified",
|
||||
"columnName": "lastModified",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mayBind",
|
||||
"columnName": "mayBind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mayUnbind",
|
||||
"columnName": "mayUnbind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mayWriteContent",
|
||||
"columnName": "mayWriteContent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "quotaAvailable",
|
||||
"columnName": "quotaAvailable",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "quotaUsed",
|
||||
"columnName": "quotaUsed",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_webdav_document_mountId_parentId_name",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"mountId",
|
||||
"parentId",
|
||||
"name"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_webdav_document_mountId_parentId_name` ON `${TABLE_NAME}` (`mountId`, `parentId`, `name`)"
|
||||
},
|
||||
{
|
||||
"name": "index_webdav_document_parentId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"parentId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_webdav_document_parentId` ON `${TABLE_NAME}` (`parentId`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "webdav_mount",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"mountId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "webdav_document",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"parentId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "webdav_mount",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9a0eb47f27473eab254db568081a4585')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,298 +0,0 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 8,
|
||||
"identityHash": "b8699ef3cc4c62e8851df4360fb69e00",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "service",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `accountName` TEXT NOT NULL, `type` TEXT NOT NULL, `principal` TEXT)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountName",
|
||||
"columnName": "accountName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "principal",
|
||||
"columnName": "principal",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_service_accountName_type",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"accountName",
|
||||
"type"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_service_accountName_type` ON `${TABLE_NAME}` (`accountName`, `type`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "homeset",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `serviceId` INTEGER NOT NULL, `personal` INTEGER NOT NULL, `url` TEXT NOT NULL, `privBind` INTEGER NOT NULL, `displayName` TEXT, FOREIGN KEY(`serviceId`) REFERENCES `service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceId",
|
||||
"columnName": "serviceId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "personal",
|
||||
"columnName": "personal",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privBind",
|
||||
"columnName": "privBind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_homeset_serviceId_url",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"serviceId",
|
||||
"url"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_homeset_serviceId_url` ON `${TABLE_NAME}` (`serviceId`, `url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "service",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"serviceId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "collection",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `serviceId` INTEGER NOT NULL, `homeSetId` INTEGER, `type` TEXT NOT NULL, `url` TEXT NOT NULL, `privWriteContent` INTEGER NOT NULL, `privUnbind` INTEGER NOT NULL, `forceReadOnly` INTEGER NOT NULL, `displayName` TEXT, `description` TEXT, `owner` TEXT, `color` INTEGER, `timezone` TEXT, `supportsVEVENT` INTEGER, `supportsVTODO` INTEGER, `supportsVJOURNAL` INTEGER, `source` TEXT, `sync` INTEGER NOT NULL, FOREIGN KEY(`serviceId`) REFERENCES `service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`homeSetId`) REFERENCES `homeset`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceId",
|
||||
"columnName": "serviceId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "homeSetId",
|
||||
"columnName": "homeSetId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privWriteContent",
|
||||
"columnName": "privWriteContent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privUnbind",
|
||||
"columnName": "privUnbind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "forceReadOnly",
|
||||
"columnName": "forceReadOnly",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "description",
|
||||
"columnName": "description",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "owner",
|
||||
"columnName": "owner",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "color",
|
||||
"columnName": "color",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "timezone",
|
||||
"columnName": "timezone",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVEVENT",
|
||||
"columnName": "supportsVEVENT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVTODO",
|
||||
"columnName": "supportsVTODO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVJOURNAL",
|
||||
"columnName": "supportsVJOURNAL",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "source",
|
||||
"columnName": "source",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "sync",
|
||||
"columnName": "sync",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_collection_serviceId_type",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"serviceId",
|
||||
"type"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_serviceId_type` ON `${TABLE_NAME}` (`serviceId`, `type`)"
|
||||
},
|
||||
{
|
||||
"name": "index_collection_homeSetId_type",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"homeSetId",
|
||||
"type"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_homeSetId_type` ON `${TABLE_NAME}` (`homeSetId`, `type`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "service",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"serviceId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "homeset",
|
||||
"onDelete": "SET NULL",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"homeSetId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b8699ef3cc4c62e8851df4360fb69e00')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,366 +0,0 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 9,
|
||||
"identityHash": "7e4bfdf7f9fa3529c333cf9485f8cf50",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "service",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `accountName` TEXT NOT NULL, `type` TEXT NOT NULL, `principal` TEXT)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountName",
|
||||
"columnName": "accountName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "principal",
|
||||
"columnName": "principal",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_service_accountName_type",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"accountName",
|
||||
"type"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_service_accountName_type` ON `${TABLE_NAME}` (`accountName`, `type`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "homeset",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `serviceId` INTEGER NOT NULL, `personal` INTEGER NOT NULL, `url` TEXT NOT NULL, `privBind` INTEGER NOT NULL, `displayName` TEXT, FOREIGN KEY(`serviceId`) REFERENCES `service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceId",
|
||||
"columnName": "serviceId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "personal",
|
||||
"columnName": "personal",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privBind",
|
||||
"columnName": "privBind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_homeset_serviceId_url",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"serviceId",
|
||||
"url"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_homeset_serviceId_url` ON `${TABLE_NAME}` (`serviceId`, `url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "service",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"serviceId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "collection",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `serviceId` INTEGER NOT NULL, `homeSetId` INTEGER, `type` TEXT NOT NULL, `url` TEXT NOT NULL, `privWriteContent` INTEGER NOT NULL, `privUnbind` INTEGER NOT NULL, `forceReadOnly` INTEGER NOT NULL, `displayName` TEXT, `description` TEXT, `owner` TEXT, `color` INTEGER, `timezone` TEXT, `supportsVEVENT` INTEGER, `supportsVTODO` INTEGER, `supportsVJOURNAL` INTEGER, `source` TEXT, `sync` INTEGER NOT NULL, FOREIGN KEY(`serviceId`) REFERENCES `service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`homeSetId`) REFERENCES `homeset`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceId",
|
||||
"columnName": "serviceId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "homeSetId",
|
||||
"columnName": "homeSetId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privWriteContent",
|
||||
"columnName": "privWriteContent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "privUnbind",
|
||||
"columnName": "privUnbind",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "forceReadOnly",
|
||||
"columnName": "forceReadOnly",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "description",
|
||||
"columnName": "description",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "owner",
|
||||
"columnName": "owner",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "color",
|
||||
"columnName": "color",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "timezone",
|
||||
"columnName": "timezone",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVEVENT",
|
||||
"columnName": "supportsVEVENT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVTODO",
|
||||
"columnName": "supportsVTODO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "supportsVJOURNAL",
|
||||
"columnName": "supportsVJOURNAL",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "source",
|
||||
"columnName": "source",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "sync",
|
||||
"columnName": "sync",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_collection_serviceId_type",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"serviceId",
|
||||
"type"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_serviceId_type` ON `${TABLE_NAME}` (`serviceId`, `type`)"
|
||||
},
|
||||
{
|
||||
"name": "index_collection_homeSetId_type",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"homeSetId",
|
||||
"type"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_homeSetId_type` ON `${TABLE_NAME}` (`homeSetId`, `type`)"
|
||||
},
|
||||
{
|
||||
"name": "index_collection_url",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"url"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_url` ON `${TABLE_NAME}` (`url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "service",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"serviceId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "homeset",
|
||||
"onDelete": "SET NULL",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"homeSetId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "syncstats",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `collectionId` INTEGER NOT NULL, `authority` TEXT NOT NULL, `lastSync` INTEGER NOT NULL, FOREIGN KEY(`collectionId`) REFERENCES `collection`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "collectionId",
|
||||
"columnName": "collectionId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "authority",
|
||||
"columnName": "authority",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastSync",
|
||||
"columnName": "lastSync",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_syncstats_collectionId_authority",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"collectionId",
|
||||
"authority"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_syncstats_collectionId_authority` ON `${TABLE_NAME}` (`collectionId`, `authority`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "collection",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"collectionId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '7e4bfdf7f9fa3529c333cf9485f8cf50')"
|
||||
]
|
||||
}
|
||||
}
|
||||
1
app/src/.gitignore
vendored
1
app/src/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
espressoTest
|
||||
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:installLocation="internalOnly">
|
||||
|
||||
<!-- account management permissions not required for own accounts since API level 22 -->
|
||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" android:maxSdkVersion="22"/>
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" android:maxSdkVersion="22"/>
|
||||
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" android:maxSdkVersion="22"/>
|
||||
|
||||
<application>
|
||||
|
||||
<!-- test account type (without associated sync adapters) -->
|
||||
<service
|
||||
android:name="at.bitfire.davdroid.sync.account.TestAccountAuthenticator"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.accounts.AccountAuthenticator"/>
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.accounts.AccountAuthenticator"
|
||||
android:resource="@xml/test_account_authenticator"/>
|
||||
</service>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright © Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import at.bitfire.cert4android.CustomCertManager;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
|
||||
import static android.support.test.InstrumentationRegistry.getInstrumentation;
|
||||
import static android.support.test.InstrumentationRegistry.getTargetContext;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static org.apache.commons.lang3.ArrayUtils.contains;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class SSLSocketFactoryCompatTest {
|
||||
|
||||
CustomCertManager certMgr;
|
||||
SSLSocketFactoryCompat factory;
|
||||
MockWebServer server = new MockWebServer();
|
||||
|
||||
@Before
|
||||
public void startServer() throws Exception {
|
||||
certMgr = new CustomCertManager(getInstrumentation().getContext(), true, null);
|
||||
factory = new SSLSocketFactoryCompat(certMgr);
|
||||
server.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopServer() throws Exception {
|
||||
server.shutdown();
|
||||
certMgr.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testUpgradeTLS() throws IOException {
|
||||
Socket s = factory.createSocket(server.getHostName(), server.getPort());
|
||||
assertTrue(s instanceof SSLSocket);
|
||||
|
||||
SSLSocket ssl = (SSLSocket)s;
|
||||
assertFalse(contains(ssl.getEnabledProtocols(), "SSLv3"));
|
||||
assertTrue(contains(ssl.getEnabledProtocols(), "TLSv1"));
|
||||
assertTrue(contains(ssl.getEnabledProtocols(), "TLSv1.1"));
|
||||
assertTrue(contains(ssl.getEnabledProtocols(), "TLSv1.2"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright © Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.model;
|
||||
|
||||
import android.content.ContentValues;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import at.bitfire.dav4android.DavResource;
|
||||
import at.bitfire.dav4android.exception.DavException;
|
||||
import at.bitfire.dav4android.exception.HttpException;
|
||||
import at.bitfire.dav4android.property.ResourceType;
|
||||
import at.bitfire.davdroid.HttpClient;
|
||||
import at.bitfire.davdroid.model.ServiceDB.Collections;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class CollectionInfoTest {
|
||||
|
||||
HttpClient httpClient;
|
||||
MockWebServer server = new MockWebServer();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
httpClient = new HttpClient.Builder().build();
|
||||
}
|
||||
|
||||
@After
|
||||
public void shutDown() {
|
||||
httpClient.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testFromDavResource() throws IOException, HttpException, DavException {
|
||||
// r/w address book
|
||||
server.enqueue(new MockResponse()
|
||||
.setResponseCode(207)
|
||||
.setBody("<multistatus xmlns='DAV:' xmlns:CARD='urn:ietf:params:xml:ns:carddav'>" +
|
||||
"<response>" +
|
||||
" <href>/</href>" +
|
||||
" <propstat><prop>" +
|
||||
" <resourcetype><collection/><CARD:addressbook/></resourcetype>" +
|
||||
" <displayname>My Contacts</displayname>" +
|
||||
" <CARD:addressbook-description>My Contacts Description</CARD:addressbook-description>" +
|
||||
" </prop></propstat>" +
|
||||
"</response>" +
|
||||
"</multistatus>"));
|
||||
|
||||
DavResource dav = new DavResource(httpClient.getOkHttpClient(), server.url("/"));
|
||||
dav.propfind(0, ResourceType.NAME);
|
||||
CollectionInfo info = new CollectionInfo(dav);
|
||||
assertEquals(CollectionInfo.Type.ADDRESS_BOOK, info.getType());
|
||||
assertFalse(info.getReadOnly());
|
||||
assertEquals("My Contacts", info.getDisplayName());
|
||||
assertEquals("My Contacts Description", info.getDescription());
|
||||
|
||||
// read-only calendar, no display name
|
||||
server.enqueue(new MockResponse()
|
||||
.setResponseCode(207)
|
||||
.setBody("<multistatus xmlns='DAV:' xmlns:CAL='urn:ietf:params:xml:ns:caldav' xmlns:ICAL='http://apple.com/ns/ical/'>" +
|
||||
"<response>" +
|
||||
" <href>/</href>" +
|
||||
" <propstat><prop>" +
|
||||
" <resourcetype><collection/><CAL:calendar/></resourcetype>" +
|
||||
" <current-user-privilege-set><privilege><read/></privilege></current-user-privilege-set>" +
|
||||
" <CAL:calendar-description>My Calendar</CAL:calendar-description>" +
|
||||
" <CAL:calendar-timezone>tzdata</CAL:calendar-timezone>" +
|
||||
" <ICAL:calendar-color>#ff0000</ICAL:calendar-color>" +
|
||||
" </prop></propstat>" +
|
||||
"</response>" +
|
||||
"</multistatus>"));
|
||||
|
||||
dav = new DavResource(httpClient.getOkHttpClient(), server.url("/"));
|
||||
dav.propfind(0, ResourceType.NAME);
|
||||
info = new CollectionInfo(dav);
|
||||
assertEquals(CollectionInfo.Type.CALENDAR, info.getType());
|
||||
assertTrue(info.getReadOnly());
|
||||
assertNull(info.getDisplayName());
|
||||
assertEquals("My Calendar", info.getDescription());
|
||||
assertEquals(0xFFFF0000, (int)info.getColor());
|
||||
assertEquals("tzdata", info.getTimeZone());
|
||||
assertTrue(info.getSupportsVEVENT());
|
||||
assertTrue(info.getSupportsVTODO());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromDB() {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(Collections.ID, 1);
|
||||
values.put(Collections.SERVICE_ID, 1);
|
||||
values.put(Collections.TYPE, CollectionInfo.Type.CALENDAR.name());
|
||||
values.put(Collections.URL, "http://example.com");
|
||||
values.put(Collections.READ_ONLY, 1);
|
||||
values.put(Collections.DISPLAY_NAME, "display name");
|
||||
values.put(Collections.DESCRIPTION, "description");
|
||||
values.put(Collections.COLOR, 0xFFFF0000);
|
||||
values.put(Collections.TIME_ZONE, "tzdata");
|
||||
values.put(Collections.SUPPORTS_VEVENT, 1);
|
||||
values.put(Collections.SUPPORTS_VTODO, 1);
|
||||
values.put(Collections.SYNC, 1);
|
||||
|
||||
CollectionInfo info = new CollectionInfo(values);
|
||||
assertEquals(CollectionInfo.Type.CALENDAR, info.getType());
|
||||
assertEquals(1, (long)info.getId());
|
||||
assertEquals(1, (long)info.getServiceID());
|
||||
assertEquals("http://example.com", info.getUrl());
|
||||
assertTrue(info.getReadOnly());
|
||||
assertEquals("display name", info.getDisplayName());
|
||||
assertEquals("description", info.getDescription());
|
||||
assertEquals(0xFFFF0000, (int)info.getColor());
|
||||
assertEquals("tzdata", info.getTimeZone());
|
||||
assertTrue(info.getSupportsVEVENT());
|
||||
assertTrue(info.getSupportsVTODO());
|
||||
assertTrue(info.getSelected());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright © Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.settings
|
||||
|
||||
import at.bitfire.davdroid.App
|
||||
import junit.framework.Assert.assertEquals
|
||||
import junit.framework.Assert.assertFalse
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultsProviderTest {
|
||||
|
||||
private val provider: Provider = DefaultsProvider()
|
||||
|
||||
@Test
|
||||
fun testHas() {
|
||||
assertEquals(Pair(false, true), provider.has("notExisting"))
|
||||
assertEquals(Pair(true, true), provider.has(App.OVERRIDE_PROXY))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGet() {
|
||||
assertEquals(Pair("localhost", true), provider.getString(App.OVERRIDE_PROXY_HOST))
|
||||
assertEquals(Pair(8118, true), provider.getInt(App.OVERRIDE_PROXY_PORT))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPutRemove() {
|
||||
assertEquals(Pair(false, true), provider.isWritable(App.OVERRIDE_PROXY))
|
||||
assertFalse(provider.putBoolean(App.OVERRIDE_PROXY, true))
|
||||
assertFalse(provider.remove(App.OVERRIDE_PROXY))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright © Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.settings
|
||||
|
||||
import android.content.ServiceConnection
|
||||
import android.support.test.InstrumentationRegistry
|
||||
import android.support.test.InstrumentationRegistry.getTargetContext
|
||||
import at.bitfire.davdroid.App
|
||||
import junit.framework.Assert.*
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
class SettingsTest {
|
||||
|
||||
lateinit var settings: Settings.Stub
|
||||
|
||||
@Before
|
||||
fun init() {
|
||||
InstrumentationRegistry.getContext().isRestricted
|
||||
settings = Settings.getInstance(getTargetContext())!!
|
||||
}
|
||||
|
||||
@After
|
||||
fun shutdown() {
|
||||
settings.close()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testHas() {
|
||||
assertFalse(settings.has("notExisting"))
|
||||
|
||||
// provided by DefaultsProvider
|
||||
assertTrue(settings.has(App.OVERRIDE_PROXY))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright © Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.ui.setup;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
import at.bitfire.dav4android.DavResource;
|
||||
import at.bitfire.dav4android.exception.DavException;
|
||||
import at.bitfire.dav4android.exception.HttpException;
|
||||
import at.bitfire.dav4android.property.AddressbookHomeSet;
|
||||
import at.bitfire.dav4android.property.ResourceType;
|
||||
import at.bitfire.davdroid.HttpClient;
|
||||
import at.bitfire.davdroid.log.Logger;
|
||||
import at.bitfire.davdroid.ui.setup.DavResourceFinder.Configuration.ServiceInfo;
|
||||
import okhttp3.mockwebserver.Dispatcher;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import okhttp3.mockwebserver.RecordedRequest;
|
||||
|
||||
import static android.support.test.InstrumentationRegistry.getTargetContext;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class DavResourceFinderTest {
|
||||
|
||||
MockWebServer server = new MockWebServer();
|
||||
|
||||
DavResourceFinder finder;
|
||||
HttpClient client;
|
||||
LoginCredentials credentials;
|
||||
|
||||
private static final String
|
||||
PATH_NO_DAV = "/nodav",
|
||||
|
||||
PATH_CALDAV = "/caldav",
|
||||
PATH_CARDDAV = "/carddav",
|
||||
PATH_CALDAV_AND_CARDDAV = "/both-caldav-carddav",
|
||||
|
||||
SUBPATH_PRINCIPAL = "/principal",
|
||||
SUBPATH_ADDRESSBOOK_HOMESET = "/addressbooks",
|
||||
SUBPATH_ADDRESSBOOK = "/addressbooks/private-contacts";
|
||||
|
||||
@Before
|
||||
public void initServerAndClient() throws Exception {
|
||||
server.setDispatcher(new TestDispatcher());
|
||||
server.start();
|
||||
|
||||
credentials = new LoginCredentials(URI.create("/"), "mock", "12345");
|
||||
finder = new DavResourceFinder(getTargetContext(), credentials);
|
||||
|
||||
client = new HttpClient.Builder()
|
||||
.addAuthentication(null, credentials.getUserName(), credentials.getPassword())
|
||||
.build();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopServer() throws Exception {
|
||||
server.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRememberIfAddressBookOrHomeset() throws IOException, HttpException, DavException {
|
||||
ServiceInfo info;
|
||||
|
||||
// before dav.propfind(), no info is available
|
||||
DavResource dav = new DavResource(client.getOkHttpClient(), server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL));
|
||||
finder.rememberIfAddressBookOrHomeset(dav, info = new ServiceInfo());
|
||||
assertEquals(0, info.getCollections().size());
|
||||
assertEquals(0, info.getHomeSets().size());
|
||||
|
||||
// recognize home set
|
||||
dav.propfind(0, AddressbookHomeSet.NAME);
|
||||
finder.rememberIfAddressBookOrHomeset(dav, info = new ServiceInfo());
|
||||
assertEquals(0, info.getCollections().size());
|
||||
assertEquals(1, info.getHomeSets().size());
|
||||
assertEquals(server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK_HOMESET + "/").uri(), info.getHomeSets().iterator().next());
|
||||
|
||||
// recognize address book
|
||||
dav = new DavResource(client.getOkHttpClient(), server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK));
|
||||
dav.propfind(0, ResourceType.NAME);
|
||||
finder.rememberIfAddressBookOrHomeset(dav, info = new ServiceInfo());
|
||||
assertEquals(1, info.getCollections().size());
|
||||
assertEquals(server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK + "/").uri(), info.getCollections().keySet().iterator().next());
|
||||
assertEquals(0, info.getHomeSets().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProvidesService() throws IOException {
|
||||
assertFalse(finder.providesService(server.url(PATH_NO_DAV), DavResourceFinder.Service.CALDAV));
|
||||
assertFalse(finder.providesService(server.url(PATH_NO_DAV), DavResourceFinder.Service.CARDDAV));
|
||||
|
||||
assertTrue(finder.providesService(server.url(PATH_CALDAV), DavResourceFinder.Service.CALDAV));
|
||||
assertFalse(finder.providesService(server.url(PATH_CALDAV), DavResourceFinder.Service.CARDDAV));
|
||||
|
||||
assertTrue(finder.providesService(server.url(PATH_CARDDAV), DavResourceFinder.Service.CARDDAV));
|
||||
assertFalse(finder.providesService(server.url(PATH_CARDDAV), DavResourceFinder.Service.CALDAV));
|
||||
|
||||
assertTrue(finder.providesService(server.url(PATH_CALDAV_AND_CARDDAV), DavResourceFinder.Service.CALDAV));
|
||||
assertTrue(finder.providesService(server.url(PATH_CALDAV_AND_CARDDAV), DavResourceFinder.Service.CARDDAV));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCurrentUserPrincipal() throws IOException, HttpException, DavException {
|
||||
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_NO_DAV), DavResourceFinder.Service.CALDAV));
|
||||
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_NO_DAV), DavResourceFinder.Service.CARDDAV));
|
||||
|
||||
assertEquals(
|
||||
server.url(PATH_CALDAV + SUBPATH_PRINCIPAL).uri(),
|
||||
finder.getCurrentUserPrincipal(server.url(PATH_CALDAV), DavResourceFinder.Service.CALDAV)
|
||||
);
|
||||
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_CALDAV), DavResourceFinder.Service.CARDDAV));
|
||||
|
||||
assertEquals(
|
||||
server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL).uri(),
|
||||
finder.getCurrentUserPrincipal(server.url(PATH_CARDDAV), DavResourceFinder.Service.CARDDAV)
|
||||
);
|
||||
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_CARDDAV), DavResourceFinder.Service.CALDAV));
|
||||
}
|
||||
|
||||
|
||||
// mock server
|
||||
|
||||
public class TestDispatcher extends Dispatcher {
|
||||
|
||||
@Override
|
||||
public MockResponse dispatch(RecordedRequest rq) throws InterruptedException {
|
||||
if (!checkAuth(rq)) {
|
||||
MockResponse authenticate = new MockResponse().setResponseCode(401);
|
||||
authenticate.setHeader("WWW-Authenticate", "Basic realm=\"test\"");
|
||||
return authenticate;
|
||||
}
|
||||
|
||||
String path = rq.getPath();
|
||||
|
||||
if ("OPTIONS".equalsIgnoreCase(rq.getMethod())) {
|
||||
String dav = null;
|
||||
if (path.startsWith(PATH_CALDAV))
|
||||
dav = "calendar-access";
|
||||
else if (path.startsWith(PATH_CARDDAV))
|
||||
dav = "addressbook";
|
||||
else if (path.startsWith(PATH_CALDAV_AND_CARDDAV))
|
||||
dav = "calendar-access, addressbook";
|
||||
MockResponse response = new MockResponse().setResponseCode(200);
|
||||
if (dav != null)
|
||||
response.addHeader("DAV", dav);
|
||||
return response;
|
||||
|
||||
} else if ("PROPFIND".equalsIgnoreCase(rq.getMethod())) {
|
||||
String props = null;
|
||||
switch (path) {
|
||||
case PATH_CALDAV:
|
||||
case PATH_CARDDAV:
|
||||
props = "<current-user-principal><href>" + path + SUBPATH_PRINCIPAL + "</href></current-user-principal>";
|
||||
break;
|
||||
|
||||
case PATH_CARDDAV + SUBPATH_PRINCIPAL:
|
||||
props = "<CARD:addressbook-home-set>" +
|
||||
" <href>" + PATH_CARDDAV + SUBPATH_ADDRESSBOOK_HOMESET + "</href>" +
|
||||
"</CARD:addressbook-home-set>";
|
||||
break;
|
||||
case PATH_CARDDAV + SUBPATH_ADDRESSBOOK:
|
||||
props = "<resourcetype>" +
|
||||
" <collection/>" +
|
||||
" <CARD:addressbook/>" +
|
||||
"</resourcetype>";
|
||||
break;
|
||||
}
|
||||
Logger.log.info("Sending props: " + props);
|
||||
return new MockResponse()
|
||||
.setResponseCode(207)
|
||||
.setBody("<multistatus xmlns='DAV:' xmlns:CARD='urn:ietf:params:xml:ns:carddav'>" +
|
||||
"<response>" +
|
||||
" <href>" + rq.getPath() + "</href>" +
|
||||
" <propstat><prop>" + props + "</prop></propstat>" +
|
||||
"</response>" +
|
||||
"</multistatus>");
|
||||
}
|
||||
|
||||
return new MockResponse().setResponseCode(404);
|
||||
}
|
||||
|
||||
private boolean checkAuth(RecordedRequest rq) {
|
||||
return "Basic bW9jazoxMjM0NQ==".equals(rq.getHeader("Authorization"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
**************************************************************************************************/
|
||||
|
||||
package at.bitfire.davdroid
|
||||
|
||||
import android.util.Xml
|
||||
import at.bitfire.dav4jvm.XmlUtils
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class Dav4jvm {
|
||||
|
||||
@Test
|
||||
fun test_Dav4jvm_XmlUtils_NewPullParser_RelaxedParsing() {
|
||||
val parser = XmlUtils.newPullParser()
|
||||
assertTrue(parser.getFeature(Xml.FEATURE_RELAXED))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import androidx.test.runner.AndroidJUnitRunner
|
||||
import dagger.hilt.android.testing.HiltTestApplication
|
||||
|
||||
class HiltTestRunner : AndroidJUnitRunner() {
|
||||
|
||||
override fun newApplication(cl: ClassLoader, name: String, context: Context): Application =
|
||||
super.newApplication(cl, HiltTestApplication::class.java.name, context)
|
||||
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid
|
||||
|
||||
import android.Manifest
|
||||
import android.accounts.Account
|
||||
import android.content.ContentProviderClient
|
||||
import android.content.ContentUris
|
||||
import android.content.ContentValues
|
||||
import android.os.Build
|
||||
import android.provider.CalendarContract
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import at.bitfire.davdroid.resource.LocalCalendar
|
||||
import at.bitfire.davdroid.resource.LocalEvent
|
||||
import at.bitfire.ical4android.AndroidCalendar
|
||||
import at.bitfire.ical4android.Event
|
||||
import net.fortuna.ical4j.model.property.DtStart
|
||||
import net.fortuna.ical4j.model.property.RRule
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.rules.ExternalResource
|
||||
import org.junit.rules.RuleChain
|
||||
import java.util.logging.Logger
|
||||
|
||||
/**
|
||||
* JUnit ClassRule which initializes the AOSP CalendarProvider.
|
||||
*
|
||||
* It seems that the calendar provider unfortunately forgets the very first requests when it is used the very first time,
|
||||
* maybe by some wrongly synchronized database initialization. So things like querying the instances
|
||||
* fails in this case.
|
||||
*
|
||||
* So this rule is needed to allow tests which need the calendar provider to succeed even when the calendar provider
|
||||
* is used the very first time (especially in CI tests / a fresh emulator).
|
||||
*
|
||||
* See [at.bitfire.davdroid.resource.LocalCalendarTest] for an example of how to use this rule.
|
||||
*/
|
||||
class InitCalendarProviderRule private constructor(): ExternalResource() {
|
||||
|
||||
companion object {
|
||||
|
||||
private var isInitialized = false
|
||||
private val logger = Logger.getLogger(InitCalendarProviderRule::javaClass.name)
|
||||
|
||||
fun getInstance(): RuleChain = RuleChain
|
||||
.outerRule(GrantPermissionRule.grant(Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR))
|
||||
.around(InitCalendarProviderRule())
|
||||
|
||||
}
|
||||
|
||||
override fun before() {
|
||||
if (!isInitialized) {
|
||||
logger.info("Initializing calendar provider")
|
||||
if (Build.VERSION.SDK_INT < 31)
|
||||
logger.warning("Calendar provider initialization may or may not work. See InitCalendarProviderRule")
|
||||
|
||||
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
val client = context.contentResolver.acquireContentProviderClient(CalendarContract.AUTHORITY)
|
||||
assertNotNull("Couldn't acquire calendar provider", client)
|
||||
|
||||
client!!.use {
|
||||
initCalendarProvider(client)
|
||||
isInitialized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initCalendarProvider(provider: ContentProviderClient) {
|
||||
val account = Account("LocalCalendarTest", CalendarContract.ACCOUNT_TYPE_LOCAL)
|
||||
|
||||
// Sometimes, the calendar provider returns an ID for the created calendar, but then fails to find it.
|
||||
var calendarOrNull: LocalCalendar? = null
|
||||
for (i in 0..50) {
|
||||
calendarOrNull = createAndVerifyCalendar(account, provider)
|
||||
if (calendarOrNull != null)
|
||||
break
|
||||
else
|
||||
Thread.sleep(100)
|
||||
}
|
||||
val calendar = calendarOrNull ?: throw IllegalStateException("Couldn't create calendar")
|
||||
|
||||
try {
|
||||
// single event init
|
||||
val normalEvent = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event with 1 instance"
|
||||
}
|
||||
val normalLocalEvent = LocalEvent(calendar, normalEvent, null, null, null, 0)
|
||||
normalLocalEvent.add()
|
||||
LocalEvent.numInstances(provider, account, normalLocalEvent.id!!)
|
||||
|
||||
// recurring event init
|
||||
val recurringEvent = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event over 22 years"
|
||||
rRules.add(RRule("FREQ=YEARLY;UNTIL=20740119T010203Z")) // year needs to be >2074 (not supported by Android <11 Calendar Storage)
|
||||
}
|
||||
val localRecurringEvent = LocalEvent(calendar, recurringEvent, null, null, null, 0)
|
||||
localRecurringEvent.add()
|
||||
LocalEvent.numInstances(provider, account, localRecurringEvent.id!!)
|
||||
} finally {
|
||||
calendar.delete()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAndVerifyCalendar(account: Account, provider: ContentProviderClient): LocalCalendar? {
|
||||
val uri = AndroidCalendar.create(account, provider, ContentValues())
|
||||
|
||||
return try {
|
||||
AndroidCalendar.findByID(
|
||||
account,
|
||||
provider,
|
||||
LocalCalendar.Factory,
|
||||
ContentUris.parseId(uri)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
logger.warning("Couldn't find calendar after creation: $e")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid
|
||||
|
||||
import android.content.Context
|
||||
import at.bitfire.davdroid.network.HttpClient
|
||||
import at.bitfire.davdroid.settings.SettingsManager
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import okhttp3.Request
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class OkhttpClientTest {
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
@ApplicationContext
|
||||
lateinit var context: Context
|
||||
|
||||
@Inject
|
||||
lateinit var settingsManager: SettingsManager
|
||||
|
||||
@Before
|
||||
fun inject() {
|
||||
hiltRule.inject()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testIcloudWithSettings() {
|
||||
val client = HttpClient.Builder(context).build()
|
||||
client.okHttpClient.newCall(Request.Builder()
|
||||
.get()
|
||||
.url("https://icloud.com")
|
||||
.build())
|
||||
.execute()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package at.bitfire.davdroid
|
||||
|
||||
import at.bitfire.davdroid.push.PushRegistrationWorker
|
||||
import at.bitfire.davdroid.repository.DavCollectionRepository
|
||||
import at.bitfire.davdroid.startup.StartupPlugin
|
||||
import at.bitfire.davdroid.startup.TasksAppWatcher
|
||||
import dagger.Module
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.hilt.testing.TestInstallIn
|
||||
import dagger.multibindings.Multibinds
|
||||
|
||||
interface TestModules {
|
||||
|
||||
// remove PushRegistrationWorkerModule from Android tests
|
||||
@Module
|
||||
@TestInstallIn(
|
||||
components = [SingletonComponent::class],
|
||||
replaces = [PushRegistrationWorker.PushRegistrationWorkerModule::class]
|
||||
)
|
||||
abstract class TestPushRegistrationWorkerModule {
|
||||
// provides empty set of listeners
|
||||
@Multibinds
|
||||
abstract fun empty(): Set<DavCollectionRepository.OnChangeListener>
|
||||
}
|
||||
|
||||
// remove TasksAppWatcherModule from Android tests
|
||||
@Module
|
||||
@TestInstallIn(
|
||||
components = [SingletonComponent::class],
|
||||
replaces = [TasksAppWatcher.TasksAppWatcherModule::class]
|
||||
)
|
||||
abstract class TestTasksAppWatcherModuleModule {
|
||||
// provides empty set of plugins
|
||||
@Multibinds
|
||||
abstract fun empty(): Set<StartupPlugin>
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkQuery
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.junit.Assert.assertTrue
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.TimeoutException
|
||||
import kotlin.math.abs
|
||||
|
||||
object TestUtils {
|
||||
|
||||
fun assertWithin(expected: Long, actual: Long, tolerance: Long) {
|
||||
val absDifference = abs(expected - actual)
|
||||
assertTrue(
|
||||
"$actual not within ($expected ± $tolerance)",
|
||||
absDifference <= tolerance
|
||||
)
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun workScheduledOrRunning(context: Context, workerName: String): Boolean =
|
||||
workInStates(context, workerName, listOf(
|
||||
WorkInfo.State.ENQUEUED,
|
||||
WorkInfo.State.RUNNING
|
||||
))
|
||||
|
||||
@TestOnly
|
||||
fun workScheduledOrRunningOrSuccessful(context: Context, workerName: String): Boolean =
|
||||
workInStates(context, workerName, listOf(
|
||||
WorkInfo.State.ENQUEUED,
|
||||
WorkInfo.State.RUNNING,
|
||||
WorkInfo.State.SUCCEEDED
|
||||
))
|
||||
|
||||
@TestOnly
|
||||
fun workInStates(context: Context, workerName: String, states: List<WorkInfo.State>): Boolean =
|
||||
WorkManager.getInstance(context).getWorkInfos(WorkQuery.Builder
|
||||
.fromUniqueWorkNames(listOf(workerName))
|
||||
.addStates(states)
|
||||
.build()
|
||||
).get().isNotEmpty()
|
||||
|
||||
|
||||
/* Copyright 2019 Google LLC.
|
||||
SPDX-License-Identifier: Apache-2.0 */
|
||||
@TestOnly
|
||||
fun <T> LiveData<T>.getOrAwaitValue(
|
||||
time: Long = 2,
|
||||
timeUnit: TimeUnit = TimeUnit.SECONDS
|
||||
): T {
|
||||
var data: T? = null
|
||||
val latch = CountDownLatch(1)
|
||||
val observer = object : Observer<T> {
|
||||
override fun onChanged(value: T) {
|
||||
data = value
|
||||
latch.countDown()
|
||||
this@getOrAwaitValue.removeObserver(this)
|
||||
}
|
||||
}
|
||||
|
||||
this.observeForever(observer)
|
||||
|
||||
// Don't wait indefinitely if the LiveData is not set.
|
||||
if (!latch.await(time, timeUnit)) {
|
||||
throw TimeoutException("LiveData value was never set.")
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return data as T
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.db
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
import androidx.room.testing.MigrationTestHelper
|
||||
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class AppDatabaseTest {
|
||||
|
||||
val TEST_DB = "test"
|
||||
|
||||
val context: Context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val helper = MigrationTestHelper(
|
||||
InstrumentationRegistry.getInstrumentation(),
|
||||
AppDatabase::class.java,
|
||||
listOf(), // no auto migrations until v8
|
||||
FrameworkSQLiteOpenHelperFactory()
|
||||
)
|
||||
|
||||
|
||||
@Test
|
||||
fun testAllMigrations() {
|
||||
// DB schema is available since version 8, so create DB with v8
|
||||
helper.createDatabase(TEST_DB, 8).close()
|
||||
|
||||
val db = Room.databaseBuilder(context, AppDatabase::class.java, TEST_DB)
|
||||
// manual migrations
|
||||
.addMigrations(*AppDatabase.migrations)
|
||||
// auto-migrations that need to be specified explicitly
|
||||
.addAutoMigrationSpec(AppDatabase.AutoMigration11_12(context))
|
||||
.build()
|
||||
try {
|
||||
// open (with version 8) + migrate (to current version) database
|
||||
db.openHelper.writableDatabase
|
||||
} finally {
|
||||
db.close()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.db
|
||||
|
||||
import android.content.Context
|
||||
import android.security.NetworkSecurityPolicy
|
||||
import androidx.test.filters.SmallTest
|
||||
import at.bitfire.dav4jvm.DavResource
|
||||
import at.bitfire.dav4jvm.property.webdav.ResourceType
|
||||
import at.bitfire.davdroid.network.HttpClient
|
||||
import at.bitfire.davdroid.settings.SettingsManager
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assume
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class CollectionTest {
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
@ApplicationContext
|
||||
lateinit var context: Context
|
||||
|
||||
@Inject
|
||||
lateinit var settingsManager: SettingsManager
|
||||
|
||||
private lateinit var httpClient: HttpClient
|
||||
private val server = MockWebServer()
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
hiltRule.inject()
|
||||
|
||||
httpClient = HttpClient.Builder(context).build()
|
||||
Assume.assumeTrue(NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted)
|
||||
}
|
||||
|
||||
@After
|
||||
fun teardown() {
|
||||
httpClient.close()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
fun testFromDavResponseAddressBook() {
|
||||
// r/w address book
|
||||
server.enqueue(MockResponse()
|
||||
.setResponseCode(207)
|
||||
.setBody("<multistatus xmlns='DAV:' xmlns:CARD='urn:ietf:params:xml:ns:carddav'>" +
|
||||
"<response>" +
|
||||
" <href>/</href>" +
|
||||
" <propstat><prop>" +
|
||||
" <resourcetype><collection/><CARD:addressbook/></resourcetype>" +
|
||||
" <displayname>My Contacts</displayname>" +
|
||||
" <CARD:addressbook-description>My Contacts Description</CARD:addressbook-description>" +
|
||||
" </prop></propstat>" +
|
||||
"</response>" +
|
||||
"</multistatus>"))
|
||||
|
||||
lateinit var info: Collection
|
||||
DavResource(httpClient.okHttpClient, server.url("/"))
|
||||
.propfind(0, ResourceType.NAME) { response, _ ->
|
||||
info = Collection.fromDavResponse(response) ?: throw IllegalArgumentException()
|
||||
}
|
||||
assertEquals(Collection.TYPE_ADDRESSBOOK, info.type)
|
||||
assertTrue(info.privWriteContent)
|
||||
assertTrue(info.privUnbind)
|
||||
assertNull(info.supportsVEVENT)
|
||||
assertNull(info.supportsVTODO)
|
||||
assertNull(info.supportsVJOURNAL)
|
||||
assertEquals("My Contacts", info.displayName)
|
||||
assertEquals("My Contacts Description", info.description)
|
||||
}
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
fun testFromDavResponseCalendar() {
|
||||
// read-only calendar, no display name
|
||||
server.enqueue(MockResponse()
|
||||
.setResponseCode(207)
|
||||
.setBody("<multistatus xmlns='DAV:' xmlns:CAL='urn:ietf:params:xml:ns:caldav' xmlns:ICAL='http://apple.com/ns/ical/'>" +
|
||||
"<response>" +
|
||||
" <href>/</href>" +
|
||||
" <propstat><prop>" +
|
||||
" <resourcetype><collection/><CAL:calendar/></resourcetype>" +
|
||||
" <current-user-privilege-set><privilege><read/></privilege></current-user-privilege-set>" +
|
||||
" <CAL:calendar-description>My Calendar</CAL:calendar-description>" +
|
||||
" <CAL:calendar-timezone>tzdata</CAL:calendar-timezone>" +
|
||||
" <ICAL:calendar-color>#ff0000</ICAL:calendar-color>" +
|
||||
" </prop></propstat>" +
|
||||
"</response>" +
|
||||
"</multistatus>"))
|
||||
|
||||
lateinit var info: Collection
|
||||
DavResource(httpClient.okHttpClient, server.url("/"))
|
||||
.propfind(0, ResourceType.NAME) { response, _ ->
|
||||
info = Collection.fromDavResponse(response) ?: throw IllegalArgumentException()
|
||||
}
|
||||
assertEquals(Collection.TYPE_CALENDAR, info.type)
|
||||
assertFalse(info.privWriteContent)
|
||||
assertFalse(info.privUnbind)
|
||||
assertNull(info.displayName)
|
||||
assertEquals("My Calendar", info.description)
|
||||
assertEquals(0xFFFF0000.toInt(), info.color)
|
||||
assertEquals("tzdata", info.timezone)
|
||||
assertTrue(info.supportsVEVENT!!)
|
||||
assertTrue(info.supportsVTODO!!)
|
||||
assertTrue(info.supportsVJOURNAL!!)
|
||||
}
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
fun testFromDavResponseWebcal() {
|
||||
// Webcal subscription
|
||||
server.enqueue(MockResponse()
|
||||
.setResponseCode(207)
|
||||
.setBody("<multistatus xmlns='DAV:' xmlns:CS='http://calendarserver.org/ns/'>" +
|
||||
"<response>" +
|
||||
" <href>/webcal1</href>" +
|
||||
" <propstat><prop>" +
|
||||
" <displayname>Sample Subscription</displayname>" +
|
||||
" <resourcetype><collection/><CS:subscribed/></resourcetype>" +
|
||||
" <CS:source><href>webcals://example.com/1.ics</href></CS:source>" +
|
||||
" </prop></propstat>" +
|
||||
"</response>" +
|
||||
"</multistatus>"))
|
||||
|
||||
lateinit var info: Collection
|
||||
DavResource(httpClient.okHttpClient, server.url("/"))
|
||||
.propfind(0, ResourceType.NAME) { response, _ ->
|
||||
info = Collection.fromDavResponse(response) ?: throw IllegalArgumentException()
|
||||
}
|
||||
assertEquals(Collection.TYPE_WEBCAL, info.type)
|
||||
assertEquals("Sample Subscription", info.displayName)
|
||||
assertEquals("https://example.com/1.ics".toHttpUrl(), info.source)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.db
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.hilt.testing.TestInstallIn
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@TestInstallIn(
|
||||
components = [ SingletonComponent::class ],
|
||||
replaces = [
|
||||
AppDatabase.AppDatabaseModule::class
|
||||
]
|
||||
)
|
||||
class MemoryDbModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun inMemoryDatabase(@ApplicationContext context: Context): AppDatabase =
|
||||
Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
|
||||
// auto-migrations that need to be specified explicitly
|
||||
.addAutoMigrationSpec(AppDatabase.AutoMigration11_12(context))
|
||||
.build()
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.network
|
||||
|
||||
import android.os.Build
|
||||
import androidx.test.filters.SdkSuppress
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.xbill.DNS.ARecord
|
||||
import org.xbill.DNS.Lookup
|
||||
import org.xbill.DNS.Type
|
||||
import java.net.Inet4Address
|
||||
import java.net.InetAddress
|
||||
|
||||
class Android10ResolverTest {
|
||||
|
||||
val FQDN_DAVX5 = "www.davx5.com"
|
||||
|
||||
@Test
|
||||
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
|
||||
fun testResolveA() {
|
||||
val www = InetAddress.getAllByName(FQDN_DAVX5).filterIsInstance<Inet4Address>().first()
|
||||
|
||||
val srvLookup = Lookup(FQDN_DAVX5, Type.A)
|
||||
srvLookup.setResolver(Android10Resolver())
|
||||
val resultGeneric = srvLookup.run()
|
||||
assertEquals(1, resultGeneric.size)
|
||||
|
||||
val result = resultGeneric.first() as ARecord
|
||||
assertEquals(www, result.address)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.network
|
||||
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.xbill.DNS.DClass
|
||||
import org.xbill.DNS.Name
|
||||
import org.xbill.DNS.SRVRecord
|
||||
import org.xbill.DNS.TXTRecord
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class DnsRecordResolverTest {
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
lateinit var dnsRecordResolver: DnsRecordResolver
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
hiltRule.inject()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testBestSRVRecord_Empty() {
|
||||
assertNull(dnsRecordResolver.bestSRVRecord(emptyArray()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBestSRVRecord_MultipleRecords_Priority_Different() {
|
||||
val dns1010 = SRVRecord(
|
||||
Name.fromString("_caldavs._tcp.example.com."),
|
||||
DClass.IN, 3600, 10, 10, 8443, Name.fromString("dav1010.example.com.")
|
||||
)
|
||||
val dns2010 = SRVRecord(
|
||||
Name.fromString("_caldavs._tcp.example.com."),
|
||||
DClass.IN, 3600, 20, 20, 8443, Name.fromString("dav2010.example.com.")
|
||||
)
|
||||
|
||||
// lowest priority first
|
||||
val result = dnsRecordResolver.bestSRVRecord(arrayOf(dns1010, dns2010))
|
||||
assertEquals(dns1010, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBestSRVRecord_MultipleRecords_Priority_Same() {
|
||||
val dns1010 = SRVRecord(
|
||||
Name.fromString("_caldavs._tcp.example.com."),
|
||||
DClass.IN, 3600, 10, 10, 8443, Name.fromString("dav1010.example.com.")
|
||||
)
|
||||
val dns1020 = SRVRecord(
|
||||
Name.fromString("_caldavs._tcp.example.com."),
|
||||
DClass.IN, 3600, 10, 20, 8443, Name.fromString("dav1020.example.com.")
|
||||
)
|
||||
|
||||
// entries are selected randomly (for load balancing)
|
||||
// run 1000 times to get a good distribution
|
||||
val counts = IntArray(2)
|
||||
for (i in 0 until 1000) {
|
||||
val result = dnsRecordResolver.bestSRVRecord(arrayOf(dns1010, dns1020))
|
||||
|
||||
when (result) {
|
||||
dns1010 -> counts[0]++
|
||||
dns1020 -> counts[1]++
|
||||
}
|
||||
}
|
||||
|
||||
/* We had weights 10 and 20, so the distribution of 1000 tries should be roughly
|
||||
weight 10 fraction 1/3 expected count 333 binomial distribution (p=1/3) with 99.99% in [275..393]
|
||||
weight 20 fraction 2/3 expected count 667 binomial distribution (p=2/3) with 99.99% in [607..725]
|
||||
*/
|
||||
assertTrue(counts[0] in 275..393)
|
||||
assertTrue(counts[1] in 607..725)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBestSRVRecord_OneRecord() {
|
||||
val dns1010 = SRVRecord(
|
||||
Name.fromString("_caldavs._tcp.example.com."),
|
||||
DClass.IN, 3600, 10, 10, 8443, Name.fromString("dav1010.example.com.")
|
||||
)
|
||||
val result = dnsRecordResolver.bestSRVRecord(arrayOf(dns1010))
|
||||
assertEquals(dns1010, result)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testPathsFromTXTRecords_Empty() {
|
||||
assertTrue(dnsRecordResolver.pathsFromTXTRecords(arrayOf()).isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPathsFromTXTRecords_OnePath() {
|
||||
val result = dnsRecordResolver.pathsFromTXTRecords(arrayOf(
|
||||
TXTRecord(Name.fromString("example.com."), 0, 0L, listOf("something=else", "path=/path1"))
|
||||
)).toTypedArray()
|
||||
assertArrayEquals(arrayOf("/path1"), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPathsFromTXTRecords_TwoPaths() {
|
||||
val result = dnsRecordResolver.pathsFromTXTRecords(arrayOf(
|
||||
TXTRecord(Name.fromString("example.com."), 0, 0L, listOf("path=/path1", "something-else", "path=/path2"))
|
||||
)).toTypedArray()
|
||||
result.sort()
|
||||
assertArrayEquals(arrayOf("/path1", "/path2"), result)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.network
|
||||
|
||||
import android.content.Context
|
||||
import android.security.NetworkSecurityPolicy
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import okhttp3.Request
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assume
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class HttpClientTest {
|
||||
|
||||
lateinit var server: MockWebServer
|
||||
lateinit var httpClient: HttpClient
|
||||
|
||||
@get:Rule
|
||||
var hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
@ApplicationContext
|
||||
lateinit var context: Context
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
hiltRule.inject()
|
||||
|
||||
httpClient = HttpClient.Builder(context).build()
|
||||
|
||||
server = MockWebServer()
|
||||
server.start(30000)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
server.shutdown()
|
||||
httpClient.close()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testCookies() {
|
||||
Assume.assumeTrue(NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted)
|
||||
val url = server.url("/test")
|
||||
|
||||
// set cookie for root path (/) and /test path in first response
|
||||
server.enqueue(MockResponse()
|
||||
.setResponseCode(200)
|
||||
.addHeader("Set-Cookie", "cookie1=1; path=/")
|
||||
.addHeader("Set-Cookie", "cookie2=2")
|
||||
.setBody("Cookie set"))
|
||||
httpClient.okHttpClient.newCall(Request.Builder()
|
||||
.get().url(url)
|
||||
.build()).execute()
|
||||
assertNull(server.takeRequest().getHeader("Cookie"))
|
||||
|
||||
// cookie should be sent with second request
|
||||
// second response lets first cookie expire and overwrites second cookie
|
||||
server.enqueue(MockResponse()
|
||||
.addHeader("Set-Cookie", "cookie1=1a; path=/; Max-Age=0")
|
||||
.addHeader("Set-Cookie", "cookie2=2a")
|
||||
.setResponseCode(200))
|
||||
httpClient.okHttpClient.newCall(Request.Builder()
|
||||
.get().url(url)
|
||||
.build()).execute()
|
||||
val header = server.takeRequest().getHeader("Cookie")
|
||||
assertTrue(header == "cookie1=1; cookie2=2" || header == "cookie2=2; cookie1=1")
|
||||
|
||||
server.enqueue(MockResponse()
|
||||
.setResponseCode(200))
|
||||
httpClient.okHttpClient.newCall(Request.Builder()
|
||||
.get().url(url)
|
||||
.build()).execute()
|
||||
assertEquals("cookie2=2a", server.takeRequest().getHeader("Cookie"))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package at.bitfire.davdroid.repository
|
||||
|
||||
import android.content.Context
|
||||
import at.bitfire.davdroid.db.AppDatabase
|
||||
import at.bitfire.davdroid.db.Collection
|
||||
import at.bitfire.davdroid.db.Service
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class DavCollectionRepositoryTest {
|
||||
|
||||
@get:Rule
|
||||
var hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
lateinit var accountSettingsFactory: AccountSettings.Factory
|
||||
|
||||
@Inject
|
||||
@ApplicationContext
|
||||
lateinit var context: Context
|
||||
|
||||
@Inject
|
||||
lateinit var db: AppDatabase
|
||||
|
||||
@Inject
|
||||
lateinit var serviceRepository: DavServiceRepository
|
||||
|
||||
var service: Service? = null
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
hiltRule.inject()
|
||||
service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
}
|
||||
|
||||
@After
|
||||
fun cleanUp() {
|
||||
db.close()
|
||||
serviceRepository.deleteAll()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testOnChangeListener_setForceReadOnly() = runBlocking {
|
||||
val collectionId = db.collectionDao().insertOrUpdateByUrl(
|
||||
Collection(
|
||||
serviceId = service!!.id,
|
||||
type = Collection.TYPE_ADDRESSBOOK,
|
||||
url = "https://example.com".toHttpUrl(),
|
||||
forceReadOnly = false,
|
||||
)
|
||||
)
|
||||
val testObserver = mockk<DavCollectionRepository.OnChangeListener>(relaxed = true)
|
||||
val collectionRepository = DavCollectionRepository(accountSettingsFactory, context, db, mutableSetOf(testObserver), serviceRepository)
|
||||
|
||||
assert(db.collectionDao().get(collectionId)?.forceReadOnly == false)
|
||||
verify(exactly = 0) {
|
||||
testObserver.onCollectionsChanged()
|
||||
}
|
||||
collectionRepository.setForceReadOnly(collectionId, true)
|
||||
assert(db.collectionDao().get(collectionId)?.forceReadOnly == true)
|
||||
verify(exactly = 1) {
|
||||
testObserver.onCollectionsChanged()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Test helpers and dependencies
|
||||
|
||||
private fun createTestService(serviceType: String) : Service? {
|
||||
val service = Service(id=0, accountName="test", type=serviceType, principal = null)
|
||||
val serviceId = serviceRepository.insertOrReplace(service)
|
||||
return serviceRepository.get(serviceId)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.repository
|
||||
|
||||
import at.bitfire.davdroid.db.HomeSet
|
||||
import at.bitfire.davdroid.db.Service
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class DavHomeSetRepositoryTest {
|
||||
|
||||
@get:Rule
|
||||
var hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
lateinit var repository: DavHomeSetRepository
|
||||
|
||||
@Inject
|
||||
lateinit var serviceRepository: DavServiceRepository
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
hiltRule.inject()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testInsertOrUpdate() {
|
||||
// should insert new row or update (upsert) existing row - without changing its key!
|
||||
val serviceId = createTestService()
|
||||
|
||||
val entry1 = HomeSet(id=0, serviceId=serviceId, personal=true, url="https://example.com/1".toHttpUrl())
|
||||
val insertId1 = repository.insertOrUpdateByUrl(entry1)
|
||||
assertEquals(1L, insertId1)
|
||||
assertEquals(entry1.apply { id = 1L }, repository.getById(1L))
|
||||
|
||||
val updatedEntry1 = HomeSet(id=0, serviceId=serviceId, personal=true, url="https://example.com/1".toHttpUrl(), displayName="Updated Entry")
|
||||
val updateId1 = repository.insertOrUpdateByUrl(updatedEntry1)
|
||||
assertEquals(1L, updateId1)
|
||||
assertEquals(updatedEntry1.apply { id = 1L }, repository.getById(1L))
|
||||
|
||||
val entry2 = HomeSet(id=0, serviceId=serviceId, personal=true, url= "https://example.com/2".toHttpUrl())
|
||||
val insertId2 = repository.insertOrUpdateByUrl(entry2)
|
||||
assertEquals(2L, insertId2)
|
||||
assertEquals(entry2.apply { id = 2L }, repository.getById(2L))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDelete() {
|
||||
// should delete row with given primary key (id)
|
||||
val serviceId = createTestService()
|
||||
|
||||
val entry1 = HomeSet(id=1, serviceId=serviceId, personal=true, url= "https://example.com/1".toHttpUrl())
|
||||
|
||||
val insertId1 = repository.insertOrUpdateByUrl(entry1)
|
||||
assertEquals(1L, insertId1)
|
||||
assertEquals(entry1, repository.getById(1L))
|
||||
|
||||
repository.delete(entry1)
|
||||
assertEquals(null, repository.getById(1L))
|
||||
}
|
||||
|
||||
|
||||
private fun createTestService() : Long {
|
||||
val service = Service(id=0, accountName="test", type= Service.TYPE_CALDAV, principal = null)
|
||||
return serviceRepository.insertOrReplace(service)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.resource
|
||||
|
||||
import android.Manifest
|
||||
import android.accounts.Account
|
||||
import android.content.ContentProviderClient
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.provider.ContactsContract
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import at.bitfire.vcard4android.Contact
|
||||
import at.bitfire.vcard4android.GroupMethod
|
||||
import at.bitfire.vcard4android.LabeledProperty
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import ezvcard.property.Telephone
|
||||
import java.util.LinkedList
|
||||
import javax.inject.Inject
|
||||
import org.junit.After
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Before
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
@HiltAndroidTest
|
||||
class LocalAddressBookTest {
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
lateinit var addressbookFactory: LocalTestAddressBook.Factory
|
||||
|
||||
@Inject
|
||||
@ApplicationContext
|
||||
lateinit var context: Context
|
||||
|
||||
lateinit var addressBook: LocalTestAddressBook
|
||||
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
hiltRule.inject()
|
||||
|
||||
addressBook = addressbookFactory.create(provider, GroupMethod.CATEGORIES)
|
||||
LocalTestAddressBook.createAccount(context)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
// remove address book
|
||||
addressBook.deleteCollection()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests whether contacts are moved (and not lost) when an address book is renamed.
|
||||
*/
|
||||
@Test
|
||||
fun test_renameAccount_retainsContacts() {
|
||||
// insert contact with data row
|
||||
val uid = "12345"
|
||||
val contact = Contact(
|
||||
uid = uid,
|
||||
displayName = "Test Contact",
|
||||
phoneNumbers = LinkedList(listOf(LabeledProperty(Telephone("1234567890"))))
|
||||
)
|
||||
val uri = LocalContact(addressBook, contact, null, null, 0).add()
|
||||
val id = ContentUris.parseId(uri)
|
||||
val localContact = addressBook.findContactById(id)
|
||||
localContact.resetDirty()
|
||||
assertFalse("Contact is dirty before moving", addressBook.isContactDirty(id))
|
||||
|
||||
// rename address book
|
||||
val newName = "New Name"
|
||||
addressBook.renameAccount(newName)
|
||||
assertEquals(Account(newName, LocalTestAddressBook.ACCOUNT.type), addressBook.addressBookAccount)
|
||||
|
||||
// check whether contact is still here (including data rows) and not dirty
|
||||
val result = addressBook.findContactById(id)
|
||||
assertFalse("Contact is dirty after moving", addressBook.isContactDirty(id))
|
||||
|
||||
val contact2 = result.getContact()
|
||||
assertEquals(uid, contact2.uid)
|
||||
assertEquals("Test Contact", contact2.displayName)
|
||||
assertEquals("1234567890", contact2.phoneNumbers.first().component1().text)
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether groups are moved (and not lost) when an address book is renamed.
|
||||
*/
|
||||
@Test
|
||||
fun test_renameAccount_retainsGroups() {
|
||||
// insert group
|
||||
val localGroup = LocalGroup(addressBook, Contact(displayName = "Test Group"), null, null, 0)
|
||||
val uri = localGroup.add()
|
||||
val id = ContentUris.parseId(uri)
|
||||
|
||||
// make sure it's not dirty
|
||||
localGroup.clearDirty(null, null, null)
|
||||
assertFalse("Group is dirty before moving", addressBook.isGroupDirty(id))
|
||||
|
||||
// rename address book
|
||||
val newName = "New Name"
|
||||
addressBook.renameAccount(newName)
|
||||
assertEquals(Account(newName, LocalTestAddressBook.ACCOUNT.type), addressBook.addressBookAccount)
|
||||
|
||||
// check whether group is still here and not dirty
|
||||
val result = addressBook.findGroupById(id)
|
||||
assertFalse("Group is dirty after moving", addressBook.isGroupDirty(id))
|
||||
|
||||
val group = result.getContact()
|
||||
assertEquals("Test Group", group.displayName)
|
||||
}
|
||||
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmField
|
||||
@ClassRule
|
||||
val permissionRule = GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)!!
|
||||
|
||||
private lateinit var provider: ContentProviderClient
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun connect() {
|
||||
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
provider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)!!
|
||||
assertNotNull(provider)
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@JvmStatic
|
||||
fun disconnect() {
|
||||
provider.close()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.resource
|
||||
|
||||
import android.accounts.Account
|
||||
import android.content.ContentProviderClient
|
||||
import android.content.ContentUris
|
||||
import android.content.ContentValues
|
||||
import android.provider.CalendarContract
|
||||
import android.provider.CalendarContract.ACCOUNT_TYPE_LOCAL
|
||||
import android.provider.CalendarContract.Events
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import at.bitfire.davdroid.InitCalendarProviderRule
|
||||
import at.bitfire.ical4android.AndroidCalendar
|
||||
import at.bitfire.ical4android.Event
|
||||
import at.bitfire.ical4android.util.MiscUtils.asSyncAdapter
|
||||
import at.bitfire.ical4android.util.MiscUtils.closeCompat
|
||||
import net.fortuna.ical4j.model.property.DtStart
|
||||
import net.fortuna.ical4j.model.property.RRule
|
||||
import net.fortuna.ical4j.model.property.RecurrenceId
|
||||
import net.fortuna.ical4j.model.property.Status
|
||||
import org.junit.After
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
|
||||
class LocalCalendarTest {
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmField
|
||||
@ClassRule
|
||||
val initCalendarProviderRule: TestRule = InitCalendarProviderRule.getInstance()
|
||||
|
||||
private lateinit var provider: ContentProviderClient
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun setUpClass() {
|
||||
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
provider = context.contentResolver.acquireContentProviderClient(CalendarContract.AUTHORITY)!!
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@JvmStatic
|
||||
fun tearDownClass() {
|
||||
provider.closeCompat()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private val account = Account("LocalCalendarTest", ACCOUNT_TYPE_LOCAL)
|
||||
private lateinit var calendar: LocalCalendar
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
val uri = AndroidCalendar.create(account, provider, ContentValues())
|
||||
calendar = AndroidCalendar.findByID(account, provider, LocalCalendar.Factory, ContentUris.parseId(uri))
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
calendar.deleteCollection()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testDeleteDirtyEventsWithoutInstances_NoInstances_CancelledExceptions() {
|
||||
// create recurring event with only deleted/cancelled instances
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event with 3 instances"
|
||||
rRules.add(RRule("FREQ=DAILY;COUNT=3"))
|
||||
exceptions.add(Event().apply {
|
||||
recurrenceId = RecurrenceId("20220120T010203Z")
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Cancelled exception on 1st day"
|
||||
status = Status.VEVENT_CANCELLED
|
||||
})
|
||||
exceptions.add(Event().apply {
|
||||
recurrenceId = RecurrenceId("20220121T010203Z")
|
||||
dtStart = DtStart("20220121T010203Z")
|
||||
summary = "Cancelled exception on 2nd day"
|
||||
status = Status.VEVENT_CANCELLED
|
||||
})
|
||||
exceptions.add(Event().apply {
|
||||
recurrenceId = RecurrenceId("20220122T010203Z")
|
||||
dtStart = DtStart("20220122T010203Z")
|
||||
summary = "Cancelled exception on 3rd day"
|
||||
status = Status.VEVENT_CANCELLED
|
||||
})
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, "filename.ics", null, null, LocalResource.FLAG_REMOTELY_PRESENT)
|
||||
localEvent.add()
|
||||
val eventId = localEvent.id!!
|
||||
|
||||
// set event as dirty
|
||||
provider.update(ContentUris.withAppendedId(Events.CONTENT_URI.asSyncAdapter(account), eventId), ContentValues(1).apply {
|
||||
put(Events.DIRTY, 1)
|
||||
}, null, null)
|
||||
|
||||
// this method should mark the event as deleted
|
||||
calendar.deleteDirtyEventsWithoutInstances()
|
||||
|
||||
// verify that event is now marked as deleted
|
||||
provider.query(
|
||||
ContentUris.withAppendedId(Events.CONTENT_URI.asSyncAdapter(account), eventId),
|
||||
arrayOf(Events.DELETED), null, null, null
|
||||
)!!.use { cursor ->
|
||||
cursor.moveToNext()
|
||||
assertEquals(1, cursor.getInt(0))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
// Flaky, Needs single or rec init of CalendarProvider (InitCalendarProviderRule)
|
||||
fun testDeleteDirtyEventsWithoutInstances_Recurring_Instances() {
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event with 3 instances"
|
||||
rRules.add(RRule("FREQ=DAILY;COUNT=3"))
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, "filename.ics", null, null, LocalResource.FLAG_REMOTELY_PRESENT)
|
||||
localEvent.add()
|
||||
val eventId = localEvent.id!!
|
||||
|
||||
// set event as dirty
|
||||
provider.update(ContentUris.withAppendedId(Events.CONTENT_URI.asSyncAdapter(account), eventId), ContentValues(1).apply {
|
||||
put(Events.DIRTY, 1)
|
||||
}, null, null)
|
||||
|
||||
// this method should mark the event as deleted
|
||||
calendar.deleteDirtyEventsWithoutInstances()
|
||||
|
||||
// verify that event is not marked as deleted
|
||||
provider.query(
|
||||
ContentUris.withAppendedId(Events.CONTENT_URI.asSyncAdapter(account), eventId),
|
||||
arrayOf(Events.DELETED), null, null, null
|
||||
)!!.use { cursor ->
|
||||
cursor.moveToNext()
|
||||
assertEquals(0, cursor.getInt(0))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,484 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.resource
|
||||
|
||||
import android.accounts.Account
|
||||
import android.content.ContentProviderClient
|
||||
import android.content.ContentUris
|
||||
import android.content.ContentValues
|
||||
import android.os.Build
|
||||
import android.provider.CalendarContract
|
||||
import android.provider.CalendarContract.ACCOUNT_TYPE_LOCAL
|
||||
import android.provider.CalendarContract.Events
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import at.bitfire.davdroid.InitCalendarProviderRule
|
||||
import at.bitfire.ical4android.AndroidCalendar
|
||||
import at.bitfire.ical4android.Event
|
||||
import at.bitfire.ical4android.util.MiscUtils.closeCompat
|
||||
import at.techbee.jtx.JtxContract.asSyncAdapter
|
||||
import net.fortuna.ical4j.model.Date
|
||||
import net.fortuna.ical4j.model.DateList
|
||||
import net.fortuna.ical4j.model.parameter.Value
|
||||
import net.fortuna.ical4j.model.property.DtStart
|
||||
import net.fortuna.ical4j.model.property.ExDate
|
||||
import net.fortuna.ical4j.model.property.RRule
|
||||
import net.fortuna.ical4j.model.property.RecurrenceId
|
||||
import net.fortuna.ical4j.model.property.Status
|
||||
import org.junit.After
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
import java.util.UUID
|
||||
|
||||
class LocalEventTest {
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmField
|
||||
@ClassRule
|
||||
val initCalendarProviderRule: TestRule = InitCalendarProviderRule.getInstance()
|
||||
|
||||
private lateinit var provider: ContentProviderClient
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun setUpClass() {
|
||||
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
provider = context.contentResolver.acquireContentProviderClient(CalendarContract.AUTHORITY)!!
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@JvmStatic
|
||||
fun tearDownClass() {
|
||||
provider.closeCompat()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private val account = Account("LocalCalendarTest", ACCOUNT_TYPE_LOCAL)
|
||||
private lateinit var calendar: LocalCalendar
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
val uri = AndroidCalendar.create(account, provider, ContentValues())
|
||||
calendar = AndroidCalendar.findByID(account, provider, LocalCalendar.Factory, ContentUris.parseId(uri))
|
||||
}
|
||||
|
||||
@After
|
||||
fun removeCalendar() {
|
||||
calendar.deleteCollection()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testNumDirectInstances_SingleInstance() {
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event with 1 instance"
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, null, null, null, 0)
|
||||
localEvent.add()
|
||||
|
||||
assertEquals(1, LocalEvent.numDirectInstances(provider, account, localEvent.id!!))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNumDirectInstances_Recurring() {
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event with 5 instances"
|
||||
rRules.add(RRule("FREQ=DAILY;COUNT=5"))
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, null, null, null, 0)
|
||||
localEvent.add()
|
||||
|
||||
assertEquals(5, LocalEvent.numDirectInstances(provider, account, localEvent.id!!))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNumDirectInstances_Recurring_Endless() {
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event without end"
|
||||
rRules.add(RRule("FREQ=DAILY"))
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, null, null, null, 0)
|
||||
localEvent.add()
|
||||
|
||||
assertNull(LocalEvent.numDirectInstances(provider, account, localEvent.id!!))
|
||||
}
|
||||
|
||||
@Test
|
||||
// flaky, needs InitCalendarProviderRule
|
||||
fun testNumDirectInstances_Recurring_LateEnd() {
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event with 53 years"
|
||||
rRules.add(RRule("FREQ=YEARLY;UNTIL=20740119T010203Z")) // year 2074 is not supported by Android <11 Calendar Storage
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, null, null, null, 0)
|
||||
localEvent.add()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
|
||||
assertEquals(52, LocalEvent.numDirectInstances(provider, account, localEvent.id!!))
|
||||
else
|
||||
assertNull(LocalEvent.numDirectInstances(provider, account, localEvent.id!!))
|
||||
}
|
||||
|
||||
@Test
|
||||
// flaky, needs InitCalendarProviderRule
|
||||
fun testNumDirectInstances_Recurring_ManyInstances() {
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event with 2 years"
|
||||
rRules.add(RRule("FREQ=DAILY;UNTIL=20240120T010203Z"))
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, null, null, null, 0)
|
||||
localEvent.add()
|
||||
val number = LocalEvent.numDirectInstances(provider, account, localEvent.id!!)
|
||||
|
||||
// Some android versions (i.e. <=Q and S) return 365*2 instances (wrong, 365*2+1 => correct),
|
||||
// but we are satisfied with either result for now
|
||||
assertTrue(number == 365*2 || number == 365*2+1)
|
||||
}
|
||||
|
||||
@Test
|
||||
// flaky, needs InitCalendarProviderRule
|
||||
fun testNumDirectInstances_RecurringWithExdate() {
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart(Date("20220120T010203Z"))
|
||||
summary = "Event with 5 instances"
|
||||
rRules.add(RRule("FREQ=DAILY;COUNT=5"))
|
||||
exDates.add(ExDate(DateList("20220121T010203Z", Value.DATE_TIME)))
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, null, null, null, 0)
|
||||
localEvent.add()
|
||||
|
||||
assertEquals(4, LocalEvent.numDirectInstances(provider, account, localEvent.id!!))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNumDirectInstances_RecurringWithExceptions() {
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event with 5 instances"
|
||||
rRules.add(RRule("FREQ=DAILY;COUNT=5"))
|
||||
exceptions.add(Event().apply {
|
||||
recurrenceId = RecurrenceId("20220122T010203Z")
|
||||
dtStart = DtStart("20220122T130203Z")
|
||||
summary = "Exception on 3rd day"
|
||||
})
|
||||
exceptions.add(Event().apply {
|
||||
recurrenceId = RecurrenceId("20220124T010203Z")
|
||||
dtStart = DtStart("20220122T160203Z")
|
||||
summary = "Exception on 5th day"
|
||||
})
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, "filename.ics", null, null, 0)
|
||||
localEvent.add()
|
||||
|
||||
assertEquals(5-2, LocalEvent.numDirectInstances(provider, account, localEvent.id!!))
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
// flaky, needs InitCalendarProviderRule
|
||||
fun testNumInstances_SingleInstance() {
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event with 1 instance"
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, null, null, null, 0)
|
||||
localEvent.add()
|
||||
|
||||
assertEquals(1, LocalEvent.numInstances(provider, account, localEvent.id!!))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNumInstances_Recurring() {
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event with 5 instances"
|
||||
rRules.add(RRule("FREQ=DAILY;COUNT=5"))
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, null, null, null, 0)
|
||||
localEvent.add()
|
||||
|
||||
assertEquals(5, LocalEvent.numInstances(provider, account, localEvent.id!!))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNumInstances_Recurring_Endless() {
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event with infinite instances"
|
||||
rRules.add(RRule("FREQ=YEARLY"))
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, null, null, null, 0)
|
||||
localEvent.add()
|
||||
|
||||
assertNull(LocalEvent.numInstances(provider, account, localEvent.id!!))
|
||||
}
|
||||
|
||||
@Test
|
||||
// flaky, needs InitCalendarProviderRule
|
||||
fun testNumInstances_Recurring_LateEnd() {
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event over 22 years"
|
||||
rRules.add(RRule("FREQ=YEARLY;UNTIL=20740119T010203Z")) // year 2074 not supported by Android <11 Calendar Storage
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, null, null, null, 0)
|
||||
localEvent.add()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
|
||||
assertEquals(52, LocalEvent.numInstances(provider, account, localEvent.id!!))
|
||||
else
|
||||
assertNull(LocalEvent.numInstances(provider, account, localEvent.id!!))
|
||||
}
|
||||
|
||||
@Test
|
||||
// flaky, needs InitCalendarProviderRule
|
||||
fun testNumInstances_Recurring_ManyInstances() {
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event over two years"
|
||||
rRules.add(RRule("FREQ=DAILY;UNTIL=20240120T010203Z"))
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, null, null, null, 0)
|
||||
localEvent.add()
|
||||
|
||||
assertEquals(
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q)
|
||||
365*2 // Android <10: does not include UNTIL (incorrect!)
|
||||
else
|
||||
365*2 + 1, // Android ≥10: includes UNTIL (correct)
|
||||
LocalEvent.numInstances(provider, account, localEvent.id!!))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNumInstances_RecurringWithExceptions() {
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event with 6 instances"
|
||||
rRules.add(RRule("FREQ=DAILY;COUNT=6"))
|
||||
exceptions.add(Event().apply {
|
||||
recurrenceId = RecurrenceId("20220122T010203Z")
|
||||
dtStart = DtStart("20220122T130203Z")
|
||||
summary = "Exception on 3rd day"
|
||||
})
|
||||
exceptions.add(Event().apply {
|
||||
recurrenceId = RecurrenceId("20220124T010203Z")
|
||||
dtStart = DtStart("20220122T160203Z")
|
||||
summary = "Exception on 5th day"
|
||||
})
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, "filename.ics", null, null, 0)
|
||||
val uri = localEvent.add()
|
||||
|
||||
calendar.findById(localEvent.id!!)
|
||||
|
||||
assertEquals(6, LocalEvent.numInstances(provider, account, localEvent.id!!))
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testMarkEventAsDeleted() {
|
||||
// Create event
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "A fine event"
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, null, null, null, 0)
|
||||
localEvent.add()
|
||||
|
||||
// Delete event
|
||||
LocalEvent.markAsDeleted(provider, account, localEvent.id!!)
|
||||
|
||||
// Get the status of whether the event is deleted
|
||||
provider.query(
|
||||
ContentUris.withAppendedId(Events.CONTENT_URI, localEvent.id!!).asSyncAdapter(account),
|
||||
arrayOf(Events.DELETED),
|
||||
null,
|
||||
null, null
|
||||
)!!.use { cursor ->
|
||||
cursor.moveToFirst()
|
||||
assertEquals(1, cursor.getInt(0))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testPrepareForUpload_NoUid() {
|
||||
// create event
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event without uid"
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, null, null, null, 0)
|
||||
localEvent.add() // save it to calendar storage
|
||||
|
||||
// prepare for upload - this should generate a new random uuid, returned as filename
|
||||
val fileNameWithSuffix = localEvent.prepareForUpload()
|
||||
val fileName = fileNameWithSuffix.removeSuffix(".ics")
|
||||
|
||||
// throws an exception if fileName is not an UUID
|
||||
UUID.fromString(fileName)
|
||||
|
||||
// UID in calendar storage should be the same as file name
|
||||
provider.query(
|
||||
ContentUris.withAppendedId(Events.CONTENT_URI, localEvent.id!!).asSyncAdapter(account),
|
||||
arrayOf(Events.UID_2445), null, null, null
|
||||
)!!.use { cursor ->
|
||||
cursor.moveToFirst()
|
||||
assertEquals(fileName, cursor.getString(0))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPrepareForUpload_NormalUid() {
|
||||
// create event
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event with normal uid"
|
||||
uid = "some-event@hostname.tld" // old UID format, UUID would be new format
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, null, null, null, 0)
|
||||
localEvent.add() // save it to calendar storage
|
||||
|
||||
// prepare for upload - this should use the UID for the file name
|
||||
val fileNameWithSuffix = localEvent.prepareForUpload()
|
||||
val fileName = fileNameWithSuffix.removeSuffix(".ics")
|
||||
|
||||
assertEquals(event.uid, fileName)
|
||||
|
||||
// UID in calendar storage should still be set, too
|
||||
provider.query(
|
||||
ContentUris.withAppendedId(Events.CONTENT_URI, localEvent.id!!).asSyncAdapter(account),
|
||||
arrayOf(Events.UID_2445), null, null, null
|
||||
)!!.use { cursor ->
|
||||
cursor.moveToFirst()
|
||||
assertEquals(fileName, cursor.getString(0))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPrepareForUpload_UidHasDangerousChars() {
|
||||
// create event
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event with funny uid"
|
||||
uid = "https://www.example.com/events/asdfewfe-cxyb-ewrws-sadfrwerxyvser-asdfxye-"
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, null, null, null, 0)
|
||||
localEvent.add() // save it to calendar storage
|
||||
|
||||
// prepare for upload - this should generate a new random uuid, returned as filename
|
||||
val fileNameWithSuffix = localEvent.prepareForUpload()
|
||||
val fileName = fileNameWithSuffix.removeSuffix(".ics")
|
||||
|
||||
// throws an exception if fileName is not an UUID
|
||||
UUID.fromString(fileName)
|
||||
|
||||
// UID in calendar storage shouldn't have been changed
|
||||
provider.query(
|
||||
ContentUris.withAppendedId(Events.CONTENT_URI, localEvent.id!!).asSyncAdapter(account),
|
||||
arrayOf(Events.UID_2445), null, null, null
|
||||
)!!.use { cursor ->
|
||||
cursor.moveToFirst()
|
||||
assertEquals(event.uid, cursor.getString(0))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testDeleteDirtyEventsWithoutInstances_NoInstances_Exdate() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDeleteDirtyEventsWithoutInstances_NoInstances_CancelledExceptions() {
|
||||
// create recurring event with only deleted/cancelled instances
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event with 3 instances"
|
||||
rRules.add(RRule("FREQ=DAILY;COUNT=3"))
|
||||
exceptions.add(Event().apply {
|
||||
recurrenceId = RecurrenceId("20220120T010203Z")
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Cancelled exception on 1st day"
|
||||
status = Status.VEVENT_CANCELLED
|
||||
})
|
||||
exceptions.add(Event().apply {
|
||||
recurrenceId = RecurrenceId("20220121T010203Z")
|
||||
dtStart = DtStart("20220121T010203Z")
|
||||
summary = "Cancelled exception on 2nd day"
|
||||
status = Status.VEVENT_CANCELLED
|
||||
})
|
||||
exceptions.add(Event().apply {
|
||||
recurrenceId = RecurrenceId("20220122T010203Z")
|
||||
dtStart = DtStart("20220122T010203Z")
|
||||
summary = "Cancelled exception on 3rd day"
|
||||
status = Status.VEVENT_CANCELLED
|
||||
})
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, "filename.ics", null, null, LocalResource.FLAG_REMOTELY_PRESENT)
|
||||
localEvent.add()
|
||||
val eventId = localEvent.id!!
|
||||
|
||||
// set event as dirty
|
||||
provider.update(ContentUris.withAppendedId(Events.CONTENT_URI.asSyncAdapter(account), eventId), ContentValues(1).apply {
|
||||
put(Events.DIRTY, 1)
|
||||
}, null, null)
|
||||
|
||||
// this method should mark the event as deleted
|
||||
calendar.deleteDirtyEventsWithoutInstances()
|
||||
|
||||
// verify that event is now marked as deleted
|
||||
provider.query(
|
||||
ContentUris.withAppendedId(Events.CONTENT_URI.asSyncAdapter(account), eventId),
|
||||
arrayOf(Events.DELETED), null, null, null
|
||||
)!!.use { cursor ->
|
||||
cursor.moveToNext()
|
||||
assertEquals(1, cursor.getInt(0))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDeleteDirtyEventsWithoutInstances_Recurring_Instances() {
|
||||
val event = Event().apply {
|
||||
dtStart = DtStart("20220120T010203Z")
|
||||
summary = "Event with 3 instances"
|
||||
rRules.add(RRule("FREQ=DAILY;COUNT=3"))
|
||||
}
|
||||
val localEvent = LocalEvent(calendar, event, "filename.ics", null, null, LocalResource.FLAG_REMOTELY_PRESENT)
|
||||
localEvent.add()
|
||||
val eventId = localEvent.id!!
|
||||
|
||||
// set event as dirty
|
||||
provider.update(ContentUris.withAppendedId(Events.CONTENT_URI.asSyncAdapter(account), eventId), ContentValues(1).apply {
|
||||
put(Events.DIRTY, 1)
|
||||
}, null, null)
|
||||
|
||||
// this method should mark the event as deleted
|
||||
calendar.deleteDirtyEventsWithoutInstances()
|
||||
|
||||
// verify that event is not marked as deleted
|
||||
provider.query(
|
||||
ContentUris.withAppendedId(Events.CONTENT_URI.asSyncAdapter(account), eventId),
|
||||
arrayOf(Events.DELETED), null, null, null
|
||||
)!!.use { cursor ->
|
||||
cursor.moveToNext()
|
||||
assertEquals(0, cursor.getInt(0))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,281 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.resource
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ContentProviderClient
|
||||
import android.content.ContentUris
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.provider.ContactsContract
|
||||
import android.provider.ContactsContract.CommonDataKinds.GroupMembership
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import at.bitfire.vcard4android.BatchOperation
|
||||
import at.bitfire.vcard4android.CachedGroupMembership
|
||||
import at.bitfire.vcard4android.Contact
|
||||
import at.bitfire.vcard4android.GroupMethod
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class LocalGroupTest {
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmField
|
||||
@ClassRule
|
||||
val permissionRule = GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)!!
|
||||
|
||||
private lateinit var provider: ContentProviderClient
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun connect() {
|
||||
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
provider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)!!
|
||||
assertNotNull(provider)
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@JvmStatic
|
||||
fun disconnect() {
|
||||
provider.close()
|
||||
}
|
||||
}
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
@ApplicationContext
|
||||
lateinit var context: Context
|
||||
|
||||
@Inject
|
||||
lateinit var addressbookFactory: LocalTestAddressBook.Factory
|
||||
|
||||
|
||||
private lateinit var addressBookGroupsAsCategories: LocalTestAddressBook
|
||||
private lateinit var addressBookGroupsAsVCards: LocalTestAddressBook
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
hiltRule.inject()
|
||||
|
||||
addressBookGroupsAsCategories = addressbookFactory.create(provider, GroupMethod.CATEGORIES)
|
||||
addressBookGroupsAsVCards = addressbookFactory.create(provider, GroupMethod.GROUP_VCARDS)
|
||||
|
||||
// clear contacts
|
||||
addressBookGroupsAsCategories.clear()
|
||||
addressBookGroupsAsVCards.clear()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testApplyPendingMemberships_addPendingMembership() {
|
||||
val ab = addressBookGroupsAsVCards
|
||||
|
||||
val contact1 = LocalContact(ab, Contact().apply {
|
||||
uid = "test1"
|
||||
displayName = "Test"
|
||||
}, "test1.vcf", null, 0)
|
||||
contact1.add()
|
||||
|
||||
val group = newGroup(ab)
|
||||
// set pending membership of contact1
|
||||
ab.provider!!.update(
|
||||
ContentUris.withAppendedId(ab.groupsSyncUri(), group.id!!),
|
||||
ContentValues().apply {
|
||||
put(LocalGroup.COLUMN_PENDING_MEMBERS, LocalGroup.PendingMemberships(setOf("test1")).toString())
|
||||
},
|
||||
null, null
|
||||
)
|
||||
|
||||
// pending membership -> contact1 should be added to group
|
||||
LocalGroup.applyPendingMemberships(ab)
|
||||
|
||||
// check group membership
|
||||
ab.provider!!.query(
|
||||
ab.syncAdapterURI(ContactsContract.Data.CONTENT_URI), arrayOf(GroupMembership.GROUP_ROW_ID, GroupMembership.RAW_CONTACT_ID),
|
||||
"${GroupMembership.MIMETYPE}=?", arrayOf(GroupMembership.CONTENT_ITEM_TYPE),
|
||||
null
|
||||
)!!.use { cursor ->
|
||||
assertTrue(cursor.moveToNext())
|
||||
assertEquals(group.id, cursor.getLong(0))
|
||||
assertEquals(contact1.id, cursor.getLong(1))
|
||||
|
||||
assertFalse(cursor.moveToNext())
|
||||
}
|
||||
// check cached group membership
|
||||
ab.provider!!.query(
|
||||
ab.syncAdapterURI(ContactsContract.Data.CONTENT_URI), arrayOf(CachedGroupMembership.GROUP_ID, CachedGroupMembership.RAW_CONTACT_ID),
|
||||
"${CachedGroupMembership.MIMETYPE}=?", arrayOf(CachedGroupMembership.CONTENT_ITEM_TYPE),
|
||||
null
|
||||
)!!.use { cursor ->
|
||||
assertTrue(cursor.moveToNext())
|
||||
assertEquals(group.id, cursor.getLong(0))
|
||||
assertEquals(contact1.id, cursor.getLong(1))
|
||||
|
||||
assertFalse(cursor.moveToNext())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testApplyPendingMemberships_removeMembership() {
|
||||
val ab = addressBookGroupsAsVCards
|
||||
|
||||
val contact1 = LocalContact(ab, Contact().apply {
|
||||
uid = "test1"
|
||||
displayName = "Test"
|
||||
}, "test1.vcf", null, 0)
|
||||
contact1.add()
|
||||
|
||||
val group = newGroup(ab)
|
||||
|
||||
// add contact1 to group
|
||||
val batch = BatchOperation(ab.provider!!)
|
||||
contact1.addToGroup(batch, group.id!!)
|
||||
batch.commit()
|
||||
|
||||
// no pending memberships -> membership should be removed
|
||||
LocalGroup.applyPendingMemberships(ab)
|
||||
|
||||
// check group membership
|
||||
ab.provider!!.query(
|
||||
ab.syncAdapterURI(ContactsContract.Data.CONTENT_URI), arrayOf(GroupMembership.GROUP_ROW_ID, GroupMembership.RAW_CONTACT_ID),
|
||||
"${GroupMembership.MIMETYPE}=?", arrayOf(GroupMembership.CONTENT_ITEM_TYPE),
|
||||
null
|
||||
)!!.use { cursor ->
|
||||
assertFalse(cursor.moveToNext())
|
||||
}
|
||||
// check cached group membership
|
||||
ab.provider!!.query(
|
||||
ab.syncAdapterURI(ContactsContract.Data.CONTENT_URI), arrayOf(CachedGroupMembership.GROUP_ID, CachedGroupMembership.RAW_CONTACT_ID),
|
||||
"${CachedGroupMembership.MIMETYPE}=?", arrayOf(CachedGroupMembership.CONTENT_ITEM_TYPE),
|
||||
null
|
||||
)!!.use { cursor ->
|
||||
assertFalse(cursor.moveToNext())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testClearDirty_addCachedGroupMembership() {
|
||||
val ab = addressBookGroupsAsCategories
|
||||
val group = newGroup(ab)
|
||||
|
||||
val contact1 = LocalContact(ab, Contact().apply { displayName = "Test" }, "fn.vcf", null, 0)
|
||||
contact1.add()
|
||||
|
||||
// insert group membership, but no cached group membership
|
||||
ab.provider!!.insert(
|
||||
ab.syncAdapterURI(ContactsContract.Data.CONTENT_URI), ContentValues().apply {
|
||||
put(GroupMembership.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE)
|
||||
put(GroupMembership.RAW_CONTACT_ID, contact1.id)
|
||||
put(GroupMembership.GROUP_ROW_ID, group.id)
|
||||
}
|
||||
)
|
||||
|
||||
group.clearDirty(null, null)
|
||||
|
||||
// check cached group membership
|
||||
ab.provider!!.query(
|
||||
ab.syncAdapterURI(ContactsContract.Data.CONTENT_URI), arrayOf(CachedGroupMembership.GROUP_ID, CachedGroupMembership.RAW_CONTACT_ID),
|
||||
"${CachedGroupMembership.MIMETYPE}=?", arrayOf(CachedGroupMembership.CONTENT_ITEM_TYPE),
|
||||
null
|
||||
)!!.use { cursor ->
|
||||
assertTrue(cursor.moveToNext())
|
||||
assertEquals(group.id, cursor.getLong(0))
|
||||
assertEquals(contact1.id, cursor.getLong(1))
|
||||
|
||||
assertFalse(cursor.moveToNext())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testClearDirty_removeCachedGroupMembership() {
|
||||
val ab = addressBookGroupsAsCategories
|
||||
val group = newGroup(ab)
|
||||
|
||||
val contact1 = LocalContact(ab, Contact().apply { displayName = "Test" }, "fn.vcf", null, 0)
|
||||
contact1.add()
|
||||
|
||||
// insert cached group membership, but no group membership
|
||||
ab.provider!!.insert(
|
||||
ab.syncAdapterURI(ContactsContract.Data.CONTENT_URI), ContentValues().apply {
|
||||
put(CachedGroupMembership.MIMETYPE, CachedGroupMembership.CONTENT_ITEM_TYPE)
|
||||
put(CachedGroupMembership.RAW_CONTACT_ID, contact1.id)
|
||||
put(CachedGroupMembership.GROUP_ID, group.id)
|
||||
}
|
||||
)
|
||||
|
||||
group.clearDirty(null, null)
|
||||
|
||||
// cached group membership should be gone
|
||||
ab.provider!!.query(
|
||||
ab.syncAdapterURI(ContactsContract.Data.CONTENT_URI), arrayOf(CachedGroupMembership.GROUP_ID, CachedGroupMembership.RAW_CONTACT_ID),
|
||||
"${CachedGroupMembership.MIMETYPE}=?", arrayOf(CachedGroupMembership.CONTENT_ITEM_TYPE),
|
||||
null
|
||||
)!!.use { cursor ->
|
||||
assertFalse(cursor.moveToNext())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testMarkMembersDirty() {
|
||||
val ab = addressBookGroupsAsCategories
|
||||
val group = newGroup(ab)
|
||||
|
||||
val contact1 = LocalContact(ab, Contact().apply { displayName = "Test" }, "fn.vcf", null, 0)
|
||||
contact1.add()
|
||||
|
||||
val batch = BatchOperation(ab.provider!!)
|
||||
contact1.addToGroup(batch, group.id!!)
|
||||
batch.commit()
|
||||
|
||||
assertEquals(0, ab.findDirty().size)
|
||||
group.markMembersDirty()
|
||||
assertEquals(contact1.id, ab.findDirty().first().id)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testPrepareForUpload() {
|
||||
val group = newGroup()
|
||||
assertNull(group.getContact().uid)
|
||||
|
||||
val fileName = group.prepareForUpload()
|
||||
val newUid = group.getContact().uid
|
||||
assertNotNull(newUid)
|
||||
assertEquals("$newUid.vcf", fileName)
|
||||
}
|
||||
|
||||
|
||||
// helpers
|
||||
|
||||
private fun newGroup(addressBook: LocalAddressBook = addressBookGroupsAsCategories): LocalGroup =
|
||||
LocalGroup(addressBook,
|
||||
Contact().apply {
|
||||
displayName = "Test Group"
|
||||
}, null, null, 0
|
||||
).apply {
|
||||
add()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.resource
|
||||
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountManager
|
||||
import android.content.ContentProviderClient
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.provider.ContactsContract
|
||||
import at.bitfire.davdroid.repository.DavCollectionRepository
|
||||
import at.bitfire.davdroid.repository.DavServiceRepository
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import at.bitfire.vcard4android.GroupMethod
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import org.junit.Assert.assertTrue
|
||||
import java.io.FileNotFoundException
|
||||
import java.util.logging.Logger
|
||||
|
||||
class LocalTestAddressBook @AssistedInject constructor(
|
||||
@Assisted provider: ContentProviderClient,
|
||||
@Assisted override val groupMethod: GroupMethod,
|
||||
accountSettingsFactory: AccountSettings.Factory,
|
||||
collectionRepository: DavCollectionRepository,
|
||||
@ApplicationContext context: Context,
|
||||
logger: Logger,
|
||||
serviceRepository: DavServiceRepository
|
||||
): LocalAddressBook(ACCOUNT, provider, accountSettingsFactory, collectionRepository, context, logger, serviceRepository) {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(provider: ContentProviderClient, groupMethod: GroupMethod): LocalTestAddressBook
|
||||
}
|
||||
|
||||
override var readOnly: Boolean
|
||||
get() = false
|
||||
set(_) = throw NotImplementedError()
|
||||
|
||||
|
||||
fun clear() {
|
||||
for (contact in queryContacts(null, null))
|
||||
contact.delete()
|
||||
for (group in queryGroups(null, null))
|
||||
group.delete()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the dirty flag of the given contact.
|
||||
*
|
||||
* @return true if the contact is dirty, false otherwise
|
||||
*
|
||||
* @throws FileNotFoundException if the contact can't be found
|
||||
*/
|
||||
fun isContactDirty(id: Long): Boolean {
|
||||
val uri = ContentUris.withAppendedId(rawContactsSyncUri(), id)
|
||||
provider!!.query(uri, arrayOf(ContactsContract.RawContacts.DIRTY), null, null, null)?.use { cursor ->
|
||||
if (cursor.moveToFirst())
|
||||
return cursor.getInt(0) != 0
|
||||
}
|
||||
throw FileNotFoundException()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the dirty flag of the given contact group.
|
||||
*
|
||||
* @return true if the group is dirty, false otherwise
|
||||
*
|
||||
* @throws FileNotFoundException if the group can't be found
|
||||
*/
|
||||
fun isGroupDirty(id: Long): Boolean {
|
||||
val uri = ContentUris.withAppendedId(groupsSyncUri(), id)
|
||||
provider!!.query(uri, arrayOf(ContactsContract.Groups.DIRTY), null, null, null)?.use { cursor ->
|
||||
if (cursor.moveToFirst())
|
||||
return cursor.getInt(0) != 0
|
||||
}
|
||||
throw FileNotFoundException()
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
val ACCOUNT = Account("LocalTestAddressBook", "at.bitfire.davdroid.test")
|
||||
|
||||
fun createAccount(context: Context) {
|
||||
val am = AccountManager.get(context)
|
||||
assertTrue("Couldn't create account for local test address-book", am.addAccountExplicitly(ACCOUNT, null, null))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.resource.contactrow
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ContentProviderClient
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.provider.ContactsContract
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import at.bitfire.davdroid.resource.LocalContact
|
||||
import at.bitfire.davdroid.resource.LocalTestAddressBook
|
||||
import at.bitfire.vcard4android.CachedGroupMembership
|
||||
import at.bitfire.vcard4android.Contact
|
||||
import at.bitfire.vcard4android.GroupMethod
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Before
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class CachedGroupMembershipHandlerTest {
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmField
|
||||
@ClassRule
|
||||
val permissionRule = GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)!!
|
||||
|
||||
private lateinit var provider: ContentProviderClient
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun connect() {
|
||||
val context = InstrumentationRegistry.getInstrumentation().context
|
||||
provider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)!!
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@JvmStatic
|
||||
fun disconnect() {
|
||||
provider.close()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Inject
|
||||
lateinit var addressbookFactory: LocalTestAddressBook.Factory
|
||||
|
||||
@Inject
|
||||
@ApplicationContext
|
||||
lateinit var context: Context
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Before
|
||||
fun inject() {
|
||||
hiltRule.inject()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testMembership() {
|
||||
val addressBook = addressbookFactory.create(provider, GroupMethod.GROUP_VCARDS)
|
||||
|
||||
val contact = Contact()
|
||||
val localContact = LocalContact(addressBook, contact, null, null, 0)
|
||||
CachedGroupMembershipHandler(localContact).handle(ContentValues().apply {
|
||||
put(CachedGroupMembership.GROUP_ID, 123456)
|
||||
put(CachedGroupMembership.RAW_CONTACT_ID, 789)
|
||||
}, contact)
|
||||
assertArrayEquals(arrayOf(123456L), localContact.cachedGroupMemberships.toArray())
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.resource.contactrow
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ContentProviderClient
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.provider.ContactsContract
|
||||
import android.provider.ContactsContract.CommonDataKinds.GroupMembership
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import at.bitfire.davdroid.resource.LocalTestAddressBook
|
||||
import at.bitfire.vcard4android.Contact
|
||||
import at.bitfire.vcard4android.GroupMethod
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class GroupMembershipBuilderTest {
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmField
|
||||
@ClassRule
|
||||
val permissionRule = GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)!!
|
||||
|
||||
private lateinit var provider: ContentProviderClient
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun connect() {
|
||||
val context: Context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
provider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)!!
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@JvmStatic
|
||||
fun disconnect() {
|
||||
provider.close()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
lateinit var addressbookFactory: LocalTestAddressBook.Factory
|
||||
|
||||
@Inject
|
||||
@ApplicationContext
|
||||
lateinit var context: Context
|
||||
|
||||
@Before
|
||||
fun inject() {
|
||||
hiltRule.inject()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testCategories_GroupsAsCategories() {
|
||||
val contact = Contact().apply {
|
||||
categories += "TEST GROUP"
|
||||
}
|
||||
val addressBookGroupsAsCategories = addressbookFactory.create(provider, GroupMethod.CATEGORIES)
|
||||
GroupMembershipBuilder(Uri.EMPTY, null, contact, addressBookGroupsAsCategories, false).build().also { result ->
|
||||
assertEquals(1, result.size)
|
||||
assertEquals(GroupMembership.CONTENT_ITEM_TYPE, result[0].values[GroupMembership.MIMETYPE])
|
||||
assertEquals(addressBookGroupsAsCategories.findOrCreateGroup("TEST GROUP"), result[0].values[GroupMembership.GROUP_ROW_ID])
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCategories_GroupsAsVCards() {
|
||||
val contact = Contact().apply {
|
||||
categories += "TEST GROUP"
|
||||
}
|
||||
val addressBookGroupsAsVCards = addressbookFactory.create(provider, GroupMethod.GROUP_VCARDS)
|
||||
GroupMembershipBuilder(Uri.EMPTY, null, contact, addressBookGroupsAsVCards, false).build().also { result ->
|
||||
// group membership is constructed during post-processing
|
||||
assertEquals(0, result.size)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.resource.contactrow
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ContentProviderClient
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.provider.ContactsContract
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import at.bitfire.davdroid.resource.LocalContact
|
||||
import at.bitfire.davdroid.resource.LocalTestAddressBook
|
||||
import at.bitfire.vcard4android.CachedGroupMembership
|
||||
import at.bitfire.vcard4android.Contact
|
||||
import at.bitfire.vcard4android.GroupMethod
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Assert
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class GroupMembershipHandlerTest {
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmField
|
||||
@ClassRule
|
||||
val permissionRule = GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)!!
|
||||
|
||||
private lateinit var provider: ContentProviderClient
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun connect() {
|
||||
val context: Context = InstrumentationRegistry.getInstrumentation().context
|
||||
provider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)!!
|
||||
Assert.assertNotNull(provider)
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@JvmStatic
|
||||
fun disconnect() {
|
||||
provider.close()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Inject
|
||||
lateinit var addressbookFactory: LocalTestAddressBook.Factory
|
||||
|
||||
@Inject @ApplicationContext
|
||||
lateinit var context: Context
|
||||
|
||||
@get:Rule
|
||||
var hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Before
|
||||
fun inject() {
|
||||
hiltRule.inject()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testMembership_GroupsAsCategories() {
|
||||
val addressBookGroupsAsCategories = addressbookFactory.create(provider, GroupMethod.CATEGORIES)
|
||||
val addressBookGroupsAsCategoriesGroup = addressBookGroupsAsCategories.findOrCreateGroup("TEST GROUP")
|
||||
|
||||
val contact = Contact()
|
||||
val localContact = LocalContact(addressBookGroupsAsCategories, contact, null, null, 0)
|
||||
GroupMembershipHandler(localContact).handle(ContentValues().apply {
|
||||
put(CachedGroupMembership.GROUP_ID, addressBookGroupsAsCategoriesGroup)
|
||||
put(CachedGroupMembership.RAW_CONTACT_ID, -1)
|
||||
}, contact)
|
||||
assertArrayEquals(arrayOf(addressBookGroupsAsCategoriesGroup), localContact.groupMemberships.toArray())
|
||||
assertArrayEquals(arrayOf("TEST GROUP"), contact.categories.toArray())
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testMembership_GroupsAsVCards() {
|
||||
val addressBookGroupsAsVCards = addressbookFactory.create(provider, GroupMethod.GROUP_VCARDS)
|
||||
|
||||
val contact = Contact()
|
||||
val localContact = LocalContact(addressBookGroupsAsVCards, contact, null, null, 0)
|
||||
GroupMembershipHandler(localContact).handle(ContentValues().apply {
|
||||
put(CachedGroupMembership.GROUP_ID, 12345) // because the group name is not queried and put into CATEGORIES, the group doesn't have to really exist
|
||||
put(CachedGroupMembership.RAW_CONTACT_ID, -1)
|
||||
}, contact)
|
||||
assertArrayEquals(arrayOf(12345L), localContact.groupMemberships.toArray())
|
||||
assertTrue(contact.categories.isEmpty())
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.resource.contactrow
|
||||
|
||||
import android.net.Uri
|
||||
import at.bitfire.vcard4android.Contact
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class UnknownPropertiesBuilderTest {
|
||||
|
||||
@Test
|
||||
fun testUnknownProperties_None() {
|
||||
UnknownPropertiesBuilder(Uri.EMPTY, null, Contact(), false).build().also { result ->
|
||||
assertEquals(0, result.size)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUnknownProperties_Properties() {
|
||||
UnknownPropertiesBuilder(Uri.EMPTY, null, Contact().apply {
|
||||
unknownProperties = "X-TEST:12345"
|
||||
}, false).build().also { result ->
|
||||
assertEquals(1, result.size)
|
||||
assertEquals(UnknownProperties.CONTENT_ITEM_TYPE, result[0].values[UnknownProperties.MIMETYPE])
|
||||
assertEquals("X-TEST:12345", result[0].values[UnknownProperties.UNKNOWN_PROPERTIES])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.resource.contactrow
|
||||
|
||||
import android.content.ContentValues
|
||||
import at.bitfire.vcard4android.Contact
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Test
|
||||
|
||||
class UnknownPropertiesHandlerTest {
|
||||
|
||||
@Test
|
||||
fun testUnknownProperties_Empty() {
|
||||
val contact = Contact()
|
||||
UnknownPropertiesHandler.handle(ContentValues().apply {
|
||||
putNull(UnknownProperties.UNKNOWN_PROPERTIES)
|
||||
}, contact)
|
||||
assertNull(contact.unknownProperties)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUnknownProperties_Values() {
|
||||
val contact = Contact()
|
||||
UnknownPropertiesHandler.handle(ContentValues().apply {
|
||||
put(UnknownProperties.UNKNOWN_PROPERTIES, "X-TEST:12345")
|
||||
}, contact)
|
||||
assertEquals("X-TEST:12345", contact.unknownProperties)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,718 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.servicedetection
|
||||
|
||||
import android.content.Context
|
||||
import android.security.NetworkSecurityPolicy
|
||||
import at.bitfire.davdroid.db.AppDatabase
|
||||
import at.bitfire.davdroid.db.Collection
|
||||
import at.bitfire.davdroid.db.HomeSet
|
||||
import at.bitfire.davdroid.db.Principal
|
||||
import at.bitfire.davdroid.db.Service
|
||||
import at.bitfire.davdroid.network.HttpClient
|
||||
import at.bitfire.davdroid.settings.Settings
|
||||
import at.bitfire.davdroid.settings.SettingsManager
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.mockk.every
|
||||
import io.mockk.mockkObject
|
||||
import okhttp3.mockwebserver.Dispatcher
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import okhttp3.mockwebserver.RecordedRequest
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assume
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.util.logging.Logger
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class CollectionListRefresherTest {
|
||||
|
||||
companion object {
|
||||
private const val PATH_CALDAV = "/caldav"
|
||||
private const val PATH_CARDDAV = "/carddav"
|
||||
|
||||
private const val SUBPATH_PRINCIPAL = "/principal"
|
||||
private const val SUBPATH_PRINCIPAL_INACCESSIBLE = "/inaccessible-principal"
|
||||
private const val SUBPATH_PRINCIPAL_WITHOUT_COLLECTIONS = "/principal2"
|
||||
private const val SUBPATH_ADDRESSBOOK_HOMESET = "/addressbooks-homeset"
|
||||
private const val SUBPATH_ADDRESSBOOK_HOMESET_EMPTY = "/addressbooks-homeset-empty"
|
||||
private const val SUBPATH_ADDRESSBOOK = "/addressbooks/my-contacts"
|
||||
private const val SUBPATH_ADDRESSBOOK_INACCESSIBLE = "/addressbooks/inaccessible-contacts"
|
||||
}
|
||||
|
||||
@get:Rule
|
||||
var hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
@ApplicationContext
|
||||
lateinit var context: Context
|
||||
|
||||
@Inject
|
||||
lateinit var db: AppDatabase
|
||||
|
||||
@Inject
|
||||
lateinit var logger: Logger
|
||||
|
||||
@Inject
|
||||
lateinit var refresherFactory: CollectionListRefresher.Factory
|
||||
|
||||
@Inject
|
||||
lateinit var settings: SettingsManager
|
||||
|
||||
private val mockServer = MockWebServer()
|
||||
private lateinit var client: HttpClient
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
hiltRule.inject()
|
||||
|
||||
// Start mock web server
|
||||
mockServer.dispatcher = TestDispatcher(logger)
|
||||
mockServer.start()
|
||||
|
||||
client = HttpClient.Builder(context).build()
|
||||
|
||||
Assume.assumeTrue(NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted)
|
||||
}
|
||||
|
||||
@After
|
||||
fun teardown() {
|
||||
mockServer.shutdown()
|
||||
db.close()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testDiscoverHomesets() {
|
||||
val service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
val baseUrl = mockServer.url(PATH_CARDDAV + SUBPATH_PRINCIPAL)
|
||||
|
||||
// Query home sets
|
||||
refresherFactory.create(service, client.okHttpClient).discoverHomesets(baseUrl)
|
||||
|
||||
// Check home sets have been saved to database
|
||||
assertEquals(mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK_HOMESET/"), db.homeSetDao().getByService(service.id).first().url)
|
||||
assertEquals(1, db.homeSetDao().getByService(service.id).size)
|
||||
}
|
||||
|
||||
|
||||
// refreshHomesetsAndTheirCollections
|
||||
|
||||
@Test
|
||||
fun refreshHomesetsAndTheirCollections_addsNewCollection() {
|
||||
val service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
|
||||
// save homeset in DB
|
||||
val homesetId = db.homeSetDao().insert(
|
||||
HomeSet(id=0, service.id, true, mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK_HOMESET"))
|
||||
)
|
||||
|
||||
// Refresh
|
||||
refresherFactory.create(service, client.okHttpClient).refreshHomesetsAndTheirCollections()
|
||||
|
||||
// Check the collection defined in homeset is now in the database
|
||||
assertEquals(
|
||||
Collection(
|
||||
1,
|
||||
service.id,
|
||||
homesetId,
|
||||
1, // will have gotten an owner too
|
||||
Collection.TYPE_ADDRESSBOOK,
|
||||
mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/"),
|
||||
displayName = "My Contacts",
|
||||
description = "My Contacts Description"
|
||||
),
|
||||
db.collectionDao().getByService(service.id).first()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun refreshHomesetsAndTheirCollections_updatesExistingCollection() {
|
||||
val service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
|
||||
// save "old" collection in DB
|
||||
val collectionId = db.collectionDao().insertOrUpdateByUrl(
|
||||
Collection(
|
||||
0,
|
||||
service.id,
|
||||
null,
|
||||
null,
|
||||
Collection.TYPE_ADDRESSBOOK,
|
||||
mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/"),
|
||||
displayName = "My Contacts",
|
||||
description = "My Contacts Description"
|
||||
)
|
||||
)
|
||||
|
||||
// Refresh
|
||||
refresherFactory.create(service, client.okHttpClient).refreshHomesetsAndTheirCollections()
|
||||
|
||||
// Check the collection got updated
|
||||
assertEquals(
|
||||
Collection(
|
||||
collectionId,
|
||||
service.id,
|
||||
null,
|
||||
null,
|
||||
Collection.TYPE_ADDRESSBOOK,
|
||||
mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/"),
|
||||
displayName = "My Contacts",
|
||||
description = "My Contacts Description"
|
||||
),
|
||||
db.collectionDao().get(collectionId)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun refreshHomesetsAndTheirCollections_preservesCollectionFlags() {
|
||||
val service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
|
||||
// save "old" collection in DB - with set flags
|
||||
val collectionId = db.collectionDao().insertOrUpdateByUrl(
|
||||
Collection(
|
||||
0,
|
||||
service.id,
|
||||
null,
|
||||
null,
|
||||
Collection.TYPE_ADDRESSBOOK,
|
||||
mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/"),
|
||||
displayName = "My Contacts",
|
||||
description = "My Contacts Description",
|
||||
forceReadOnly = true,
|
||||
sync = true
|
||||
)
|
||||
)
|
||||
|
||||
// Refresh
|
||||
refresherFactory.create(service, client.okHttpClient).refreshHomesetsAndTheirCollections()
|
||||
|
||||
// Check the collection got updated
|
||||
assertEquals(
|
||||
Collection(
|
||||
collectionId,
|
||||
service.id,
|
||||
null,
|
||||
null,
|
||||
Collection.TYPE_ADDRESSBOOK,
|
||||
mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/"),
|
||||
displayName = "My Contacts",
|
||||
description = "My Contacts Description",
|
||||
forceReadOnly = true,
|
||||
sync = true
|
||||
),
|
||||
db.collectionDao().get(collectionId)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun refreshHomesetsAndTheirCollections_marksRemovedCollectionsAsHomeless() {
|
||||
val service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
|
||||
// save homeset in DB - which is empty (zero address books) on the serverside
|
||||
val homesetId = db.homeSetDao().insert(
|
||||
HomeSet(id=0, service.id, true, mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK_HOMESET_EMPTY"))
|
||||
)
|
||||
|
||||
// place collection in DB - as part of the homeset
|
||||
val collectionId = db.collectionDao().insertOrUpdateByUrl(
|
||||
Collection(
|
||||
0,
|
||||
service.id,
|
||||
homesetId,
|
||||
null,
|
||||
Collection.TYPE_ADDRESSBOOK,
|
||||
mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/")
|
||||
)
|
||||
)
|
||||
|
||||
// Refresh - should mark collection as homeless, because serverside homeset is empty.
|
||||
refresherFactory.create(service, client.okHttpClient).refreshHomesetsAndTheirCollections()
|
||||
|
||||
// Check the collection, is now marked as homeless
|
||||
assertEquals(null, db.collectionDao().get(collectionId)!!.homeSetId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun refreshHomesetsAndTheirCollections_addsOwnerUrls() {
|
||||
val service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
|
||||
// save a homeset in DB
|
||||
val homesetId = db.homeSetDao().insert(
|
||||
HomeSet(id=0, service.id, true, mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK_HOMESET"))
|
||||
)
|
||||
|
||||
// place collection in DB - as part of the homeset
|
||||
val collectionId = db.collectionDao().insertOrUpdateByUrl(
|
||||
Collection(
|
||||
0,
|
||||
service.id,
|
||||
homesetId, // part of above home set
|
||||
null,
|
||||
Collection.TYPE_ADDRESSBOOK,
|
||||
mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/")
|
||||
)
|
||||
)
|
||||
|
||||
// Refresh - homesets and their collections
|
||||
assertEquals(0, db.principalDao().getByService(service.id).size)
|
||||
refresherFactory.create(service, client.okHttpClient).refreshHomesetsAndTheirCollections()
|
||||
|
||||
// Check principal saved and the collection was updated with its reference
|
||||
val principals = db.principalDao().getByService(service.id)
|
||||
assertEquals(1, principals.size)
|
||||
assertEquals(mockServer.url("$PATH_CARDDAV$SUBPATH_PRINCIPAL"), principals[0].url)
|
||||
assertEquals(null, principals[0].displayName)
|
||||
assertEquals(
|
||||
principals[0].id,
|
||||
db.collectionDao().get(collectionId)!!.ownerId
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// refreshHomelessCollections
|
||||
|
||||
@Test
|
||||
fun refreshHomelessCollections_updatesExistingCollection() {
|
||||
val service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
|
||||
// place homeless collection in DB
|
||||
val collectionId = db.collectionDao().insertOrUpdateByUrl(
|
||||
Collection(
|
||||
0,
|
||||
service.id,
|
||||
null,
|
||||
null,
|
||||
Collection.TYPE_ADDRESSBOOK,
|
||||
mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/"),
|
||||
)
|
||||
)
|
||||
|
||||
// Refresh
|
||||
refresherFactory.create(service, client.okHttpClient).refreshHomelessCollections()
|
||||
|
||||
// Check the collection got updated - with display name and description
|
||||
assertEquals(
|
||||
Collection(
|
||||
collectionId,
|
||||
service.id,
|
||||
null,
|
||||
1, // will have gotten an owner too
|
||||
Collection.TYPE_ADDRESSBOOK,
|
||||
mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/"),
|
||||
displayName = "My Contacts",
|
||||
description = "My Contacts Description"
|
||||
),
|
||||
db.collectionDao().get(collectionId)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun refreshHomelessCollections_deletesInaccessibleCollections() {
|
||||
val service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
|
||||
// place homeless collection in DB - it is also inaccessible
|
||||
val collectionId = db.collectionDao().insertOrUpdateByUrl(
|
||||
Collection(
|
||||
0,
|
||||
service.id,
|
||||
null,
|
||||
null,
|
||||
Collection.TYPE_ADDRESSBOOK,
|
||||
mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK_INACCESSIBLE")
|
||||
)
|
||||
)
|
||||
|
||||
// Refresh - should delete collection
|
||||
refresherFactory.create(service, client.okHttpClient).refreshHomelessCollections()
|
||||
|
||||
// Check the collection got deleted
|
||||
assertEquals(null, db.collectionDao().get(collectionId))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun refreshHomelessCollections_addsOwnerUrls() {
|
||||
val service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
|
||||
// place homeless collection in DB
|
||||
val collectionId = db.collectionDao().insertOrUpdateByUrl(
|
||||
Collection(
|
||||
0,
|
||||
service.id,
|
||||
null,
|
||||
null,
|
||||
Collection.TYPE_ADDRESSBOOK,
|
||||
mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/"),
|
||||
)
|
||||
)
|
||||
|
||||
// Refresh homeless collections
|
||||
assertEquals(0, db.principalDao().getByService(service.id).size)
|
||||
refresherFactory.create(service, client.okHttpClient).refreshHomelessCollections()
|
||||
|
||||
// Check principal saved and the collection was updated with its reference
|
||||
val principals = db.principalDao().getByService(service.id)
|
||||
assertEquals(1, principals.size)
|
||||
assertEquals(mockServer.url("$PATH_CARDDAV$SUBPATH_PRINCIPAL"), principals[0].url)
|
||||
assertEquals(null, principals[0].displayName)
|
||||
assertEquals(
|
||||
principals[0].id,
|
||||
db.collectionDao().get(collectionId)!!.ownerId
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// refreshPrincipals
|
||||
|
||||
@Test
|
||||
fun refreshPrincipals_inaccessiblePrincipal() {
|
||||
val service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
|
||||
// place principal without display name in db
|
||||
val principalId = db.principalDao().insert(
|
||||
Principal(
|
||||
0,
|
||||
service.id,
|
||||
mockServer.url("$PATH_CARDDAV$SUBPATH_PRINCIPAL_INACCESSIBLE"), // no trailing slash
|
||||
null // no display name for now
|
||||
)
|
||||
)
|
||||
// add an associated collection - as the principal is rightfully removed otherwise
|
||||
db.collectionDao().insertOrUpdateByUrl(
|
||||
Collection(
|
||||
0,
|
||||
service.id,
|
||||
null,
|
||||
principalId, // create association with principal
|
||||
Collection.TYPE_ADDRESSBOOK,
|
||||
mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/"), // with trailing slash
|
||||
)
|
||||
)
|
||||
|
||||
// Refresh principals
|
||||
refresherFactory.create(service, client.okHttpClient).refreshPrincipals()
|
||||
|
||||
// Check principal was not updated
|
||||
val principals = db.principalDao().getByService(service.id)
|
||||
assertEquals(1, principals.size)
|
||||
assertEquals(mockServer.url("$PATH_CARDDAV$SUBPATH_PRINCIPAL_INACCESSIBLE"), principals[0].url)
|
||||
assertEquals(null, principals[0].displayName)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun refreshPrincipals_updatesPrincipal() {
|
||||
val service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
|
||||
// place principal without display name in db
|
||||
val principalId = db.principalDao().insert(
|
||||
Principal(
|
||||
0,
|
||||
service.id,
|
||||
mockServer.url("$PATH_CARDDAV$SUBPATH_PRINCIPAL"), // no trailing slash
|
||||
null // no display name for now
|
||||
)
|
||||
)
|
||||
// add an associated collection - as the principal is rightfully removed otherwise
|
||||
db.collectionDao().insertOrUpdateByUrl(
|
||||
Collection(
|
||||
0,
|
||||
service.id,
|
||||
null,
|
||||
principalId, // create association with principal
|
||||
Collection.TYPE_ADDRESSBOOK,
|
||||
mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/"), // with trailing slash
|
||||
)
|
||||
)
|
||||
|
||||
// Refresh principals
|
||||
refresherFactory.create(service, client.okHttpClient).refreshPrincipals()
|
||||
|
||||
// Check principal now got a display name
|
||||
val principals = db.principalDao().getByService(service.id)
|
||||
assertEquals(1, principals.size)
|
||||
assertEquals(mockServer.url("$PATH_CARDDAV$SUBPATH_PRINCIPAL"), principals[0].url)
|
||||
assertEquals("Mr. Wobbles", principals[0].displayName)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun refreshPrincipals_deletesPrincipalsWithoutCollections() {
|
||||
val service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
|
||||
// place principal without collections in DB
|
||||
db.principalDao().insert(
|
||||
Principal(
|
||||
0,
|
||||
service.id,
|
||||
mockServer.url("$PATH_CARDDAV$SUBPATH_PRINCIPAL_WITHOUT_COLLECTIONS/")
|
||||
)
|
||||
)
|
||||
|
||||
// Refresh principals - detecting it does not own collections
|
||||
refresherFactory.create(service, client.okHttpClient).refreshPrincipals()
|
||||
|
||||
// Check principal was deleted
|
||||
val principals = db.principalDao().getByService(service.id)
|
||||
assertEquals(0, principals.size)
|
||||
}
|
||||
|
||||
// Others
|
||||
|
||||
@Test
|
||||
fun shouldPreselect_none() {
|
||||
val service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
|
||||
mockkObject(settings) {
|
||||
every { settings.getIntOrNull(Settings.PRESELECT_COLLECTIONS) } returns Settings.PRESELECT_COLLECTIONS_NONE
|
||||
every { settings.getString(Settings.PRESELECT_COLLECTIONS_EXCLUDED) } returns ""
|
||||
|
||||
val collection = Collection(
|
||||
0,
|
||||
service.id,
|
||||
0,
|
||||
type = Collection.TYPE_ADDRESSBOOK,
|
||||
url = mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/")
|
||||
)
|
||||
val homesets = listOf(
|
||||
HomeSet(0, service.id, true, mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK_HOMESET"))
|
||||
)
|
||||
|
||||
val refresher = refresherFactory.create(service, client.okHttpClient)
|
||||
assertFalse(refresher.shouldPreselect(collection, homesets))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldPreselect_all() {
|
||||
val service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
|
||||
mockkObject(settings) {
|
||||
every { settings.getIntOrNull(Settings.PRESELECT_COLLECTIONS) } returns Settings.PRESELECT_COLLECTIONS_ALL
|
||||
every { settings.getString(Settings.PRESELECT_COLLECTIONS_EXCLUDED) } returns ""
|
||||
|
||||
val collection = Collection(
|
||||
0,
|
||||
service.id,
|
||||
0,
|
||||
type = Collection.TYPE_ADDRESSBOOK,
|
||||
url = mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/")
|
||||
)
|
||||
val homesets = listOf(
|
||||
HomeSet(0, service.id, false, mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK_HOMESET"))
|
||||
)
|
||||
|
||||
val refresher = refresherFactory.create(service, client.okHttpClient)
|
||||
assertTrue(refresher.shouldPreselect(collection, homesets))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldPreselect_all_blacklisted() {
|
||||
val service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
val url = mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/")
|
||||
|
||||
mockkObject(settings) {
|
||||
every { settings.getIntOrNull(Settings.PRESELECT_COLLECTIONS) } returns Settings.PRESELECT_COLLECTIONS_ALL
|
||||
every { settings.getString(Settings.PRESELECT_COLLECTIONS_EXCLUDED) } returns url.toString()
|
||||
|
||||
val collection = Collection(
|
||||
0,
|
||||
service.id,
|
||||
0,
|
||||
type = Collection.TYPE_ADDRESSBOOK,
|
||||
url = url
|
||||
)
|
||||
val homesets = listOf(
|
||||
HomeSet(0, service.id, false, mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK_HOMESET"))
|
||||
)
|
||||
|
||||
val refresher = refresherFactory.create(service, client.okHttpClient)
|
||||
assertFalse(refresher.shouldPreselect(collection, homesets))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldPreselect_personal_notPersonal() {
|
||||
val service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
|
||||
mockkObject(settings) {
|
||||
every { settings.getIntOrNull(Settings.PRESELECT_COLLECTIONS) } returns Settings.PRESELECT_COLLECTIONS_PERSONAL
|
||||
every { settings.getString(Settings.PRESELECT_COLLECTIONS_EXCLUDED) } returns ""
|
||||
|
||||
val collection = Collection(
|
||||
0,
|
||||
service.id,
|
||||
0,
|
||||
type = Collection.TYPE_ADDRESSBOOK,
|
||||
url = mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/")
|
||||
)
|
||||
val homesets = listOf(
|
||||
HomeSet(0, service.id, false, mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK_HOMESET"))
|
||||
)
|
||||
|
||||
val refresher = refresherFactory.create(service, client.okHttpClient)
|
||||
assertFalse(refresher.shouldPreselect(collection, homesets))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldPreselect_personal_isPersonal() {
|
||||
val service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
|
||||
mockkObject(settings) {
|
||||
every { settings.getIntOrNull(Settings.PRESELECT_COLLECTIONS) } returns Settings.PRESELECT_COLLECTIONS_PERSONAL
|
||||
every { settings.getString(Settings.PRESELECT_COLLECTIONS_EXCLUDED) } returns ""
|
||||
|
||||
val collection = Collection(
|
||||
0,
|
||||
service.id,
|
||||
0,
|
||||
type = Collection.TYPE_ADDRESSBOOK,
|
||||
url = mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/")
|
||||
)
|
||||
val homesets = listOf(
|
||||
HomeSet(0, service.id, true, mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK_HOMESET"))
|
||||
)
|
||||
|
||||
val refresher = refresherFactory.create(service, client.okHttpClient)
|
||||
assertTrue(refresher.shouldPreselect(collection, homesets))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldPreselect_personal_isPersonalButBlacklisted() {
|
||||
val service = createTestService(Service.TYPE_CARDDAV)!!
|
||||
val collectionUrl = mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/")
|
||||
|
||||
mockkObject(settings) {
|
||||
every { settings.getIntOrNull(Settings.PRESELECT_COLLECTIONS) } returns Settings.PRESELECT_COLLECTIONS_PERSONAL
|
||||
every { settings.getString(Settings.PRESELECT_COLLECTIONS_EXCLUDED) } returns collectionUrl.toString()
|
||||
|
||||
val collection = Collection(
|
||||
0,
|
||||
service.id,
|
||||
0,
|
||||
type = Collection.TYPE_ADDRESSBOOK,
|
||||
url = collectionUrl
|
||||
)
|
||||
val homesets = listOf(
|
||||
HomeSet(0, service.id, true, mockServer.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK_HOMESET"))
|
||||
)
|
||||
|
||||
val refresher = refresherFactory.create(service, client.okHttpClient)
|
||||
assertFalse(refresher.shouldPreselect(collection, homesets))
|
||||
}
|
||||
}
|
||||
|
||||
// Test helpers and dependencies
|
||||
|
||||
private fun createTestService(serviceType: String) : Service? {
|
||||
val service = Service(id=0, accountName="test", type=serviceType, principal = null)
|
||||
val serviceId = db.serviceDao().insertOrReplace(service)
|
||||
return db.serviceDao().get(serviceId)
|
||||
}
|
||||
|
||||
|
||||
class TestDispatcher(
|
||||
private val logger: Logger
|
||||
): Dispatcher() {
|
||||
|
||||
override fun dispatch(request: RecordedRequest): MockResponse {
|
||||
val path = request.path!!.trimEnd('/')
|
||||
|
||||
if (request.method.equals("PROPFIND", true)) {
|
||||
val properties = when (path) {
|
||||
PATH_CALDAV,
|
||||
PATH_CARDDAV ->
|
||||
"<current-user-principal>" +
|
||||
" <href>$path${SUBPATH_PRINCIPAL}</href>" +
|
||||
"</current-user-principal>"
|
||||
|
||||
PATH_CARDDAV + SUBPATH_PRINCIPAL ->
|
||||
"<resourcetype><principal/></resourcetype>" +
|
||||
"<displayname>Mr. Wobbles</displayname>" +
|
||||
"<CARD:addressbook-home-set>" +
|
||||
" <href>${PATH_CARDDAV}${SUBPATH_ADDRESSBOOK_HOMESET}</href>" +
|
||||
"</CARD:addressbook-home-set>"
|
||||
|
||||
PATH_CARDDAV + SUBPATH_PRINCIPAL_WITHOUT_COLLECTIONS ->
|
||||
"<CARD:addressbook-home-set>" +
|
||||
" <href>${PATH_CARDDAV}${SUBPATH_ADDRESSBOOK_HOMESET_EMPTY}</href>" +
|
||||
"</CARD:addressbook-home-set>" +
|
||||
"<displayname>Mr. Wobbles Jr.</displayname>"
|
||||
|
||||
PATH_CARDDAV + SUBPATH_ADDRESSBOOK,
|
||||
PATH_CARDDAV + SUBPATH_ADDRESSBOOK_HOMESET ->
|
||||
"<resourcetype>" +
|
||||
" <collection/>" +
|
||||
" <CARD:addressbook/>" +
|
||||
"</resourcetype>" +
|
||||
"<displayname>My Contacts</displayname>" +
|
||||
"<CARD:addressbook-description>My Contacts Description</CARD:addressbook-description>" +
|
||||
"<owner>" +
|
||||
" <href>${PATH_CARDDAV + SUBPATH_PRINCIPAL}</href>" +
|
||||
"</owner>"
|
||||
|
||||
PATH_CALDAV + SUBPATH_PRINCIPAL ->
|
||||
"<CAL:calendar-user-address-set>" +
|
||||
" <href>urn:unknown-entry</href>" +
|
||||
" <href>mailto:email1@example.com</href>" +
|
||||
" <href>mailto:email2@example.com</href>" +
|
||||
"</CAL:calendar-user-address-set>"
|
||||
|
||||
SUBPATH_ADDRESSBOOK_HOMESET_EMPTY -> ""
|
||||
|
||||
else -> ""
|
||||
}
|
||||
|
||||
var responseBody = ""
|
||||
var responseCode = 207
|
||||
when (path) {
|
||||
PATH_CARDDAV + SUBPATH_ADDRESSBOOK_HOMESET ->
|
||||
responseBody =
|
||||
"<multistatus xmlns='DAV:' xmlns:CARD='urn:ietf:params:xml:ns:carddav' xmlns:CAL='urn:ietf:params:xml:ns:caldav'>" +
|
||||
"<response>" +
|
||||
" <href>${PATH_CARDDAV + SUBPATH_ADDRESSBOOK}</href>" +
|
||||
" <propstat><prop>" +
|
||||
properties +
|
||||
" </prop></propstat>" +
|
||||
" <status>HTTP/1.1 200 OK</status>" +
|
||||
"</response>" +
|
||||
"</multistatus>"
|
||||
|
||||
PATH_CARDDAV + SUBPATH_PRINCIPAL_INACCESSIBLE,
|
||||
PATH_CARDDAV + SUBPATH_ADDRESSBOOK_INACCESSIBLE ->
|
||||
responseCode = 404
|
||||
|
||||
else ->
|
||||
responseBody =
|
||||
"<multistatus xmlns='DAV:' xmlns:CARD='urn:ietf:params:xml:ns:carddav' xmlns:CAL='urn:ietf:params:xml:ns:caldav'>" +
|
||||
"<response>" +
|
||||
" <href>$path</href>" +
|
||||
" <propstat><prop>"+
|
||||
properties +
|
||||
" </prop></propstat>" +
|
||||
"</response>" +
|
||||
"</multistatus>"
|
||||
}
|
||||
|
||||
logger.info("Queried: $path")
|
||||
logger.info("Response: $responseBody")
|
||||
return MockResponse()
|
||||
.setResponseCode(responseCode)
|
||||
.setBody(responseBody)
|
||||
}
|
||||
|
||||
return MockResponse().setResponseCode(404)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.servicedetection
|
||||
|
||||
import android.content.Context
|
||||
import android.security.NetworkSecurityPolicy
|
||||
import androidx.test.filters.SmallTest
|
||||
import at.bitfire.dav4jvm.DavResource
|
||||
import at.bitfire.dav4jvm.property.carddav.AddressbookHomeSet
|
||||
import at.bitfire.dav4jvm.property.webdav.ResourceType
|
||||
import at.bitfire.davdroid.db.Credentials
|
||||
import at.bitfire.davdroid.network.HttpClient
|
||||
import at.bitfire.davdroid.servicedetection.DavResourceFinder.Configuration.ServiceInfo
|
||||
import at.bitfire.davdroid.settings.SettingsManager
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import okhttp3.mockwebserver.Dispatcher
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import okhttp3.mockwebserver.RecordedRequest
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assume
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.net.URI
|
||||
import java.util.logging.Logger
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class DavResourceFinderTest {
|
||||
|
||||
companion object {
|
||||
private const val PATH_NO_DAV = "/nodav"
|
||||
private const val PATH_CALDAV = "/caldav"
|
||||
private const val PATH_CARDDAV = "/carddav"
|
||||
private const val PATH_CALDAV_AND_CARDDAV = "/both-caldav-carddav"
|
||||
|
||||
private const val SUBPATH_PRINCIPAL = "/principal"
|
||||
private const val SUBPATH_ADDRESSBOOK_HOMESET = "/addressbooks"
|
||||
private const val SUBPATH_ADDRESSBOOK = "/addressbooks/private-contacts"
|
||||
}
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
@ApplicationContext
|
||||
lateinit var context: Context
|
||||
|
||||
@Inject
|
||||
lateinit var logger: Logger
|
||||
|
||||
@Inject
|
||||
lateinit var resourceFinderFactory: DavResourceFinder.Factory
|
||||
|
||||
@Inject
|
||||
lateinit var settingsManager: SettingsManager
|
||||
|
||||
private val server = MockWebServer()
|
||||
|
||||
private lateinit var finder: DavResourceFinder
|
||||
private lateinit var client: HttpClient
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
hiltRule.inject()
|
||||
|
||||
server.dispatcher = TestDispatcher(logger)
|
||||
server.start()
|
||||
|
||||
val baseURI = URI.create("/")
|
||||
val credentials = Credentials("mock", "12345")
|
||||
|
||||
finder = resourceFinderFactory.create(baseURI, credentials)
|
||||
client = HttpClient.Builder(context)
|
||||
.addAuthentication(null, credentials)
|
||||
.build()
|
||||
|
||||
Assume.assumeTrue(NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted)
|
||||
}
|
||||
|
||||
@After
|
||||
fun teardown() {
|
||||
server.shutdown()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
fun testRememberIfAddressBookOrHomeset() {
|
||||
// recognize home set
|
||||
var info = ServiceInfo()
|
||||
DavResource(client.okHttpClient, server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL))
|
||||
.propfind(0, AddressbookHomeSet.NAME) { response, _ ->
|
||||
finder.scanResponse(ResourceType.ADDRESSBOOK, response, info)
|
||||
}
|
||||
assertEquals(0, info.collections.size)
|
||||
assertEquals(1, info.homeSets.size)
|
||||
assertEquals(server.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK_HOMESET/"), info.homeSets.first())
|
||||
|
||||
// recognize address book
|
||||
info = ServiceInfo()
|
||||
DavResource(client.okHttpClient, server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK))
|
||||
.propfind(0, ResourceType.NAME) { response, _ ->
|
||||
finder.scanResponse(ResourceType.ADDRESSBOOK, response, info)
|
||||
}
|
||||
assertEquals(1, info.collections.size)
|
||||
assertEquals(server.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/"), info.collections.keys.first())
|
||||
assertEquals(0, info.homeSets.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testProvidesService() {
|
||||
assertFalse(finder.providesService(server.url(PATH_NO_DAV), DavResourceFinder.Service.CALDAV))
|
||||
assertFalse(finder.providesService(server.url(PATH_NO_DAV), DavResourceFinder.Service.CARDDAV))
|
||||
|
||||
assertTrue(finder.providesService(server.url(PATH_CALDAV), DavResourceFinder.Service.CALDAV))
|
||||
assertFalse(finder.providesService(server.url(PATH_CALDAV), DavResourceFinder.Service.CARDDAV))
|
||||
|
||||
assertTrue(finder.providesService(server.url(PATH_CARDDAV), DavResourceFinder.Service.CARDDAV))
|
||||
assertFalse(finder.providesService(server.url(PATH_CARDDAV), DavResourceFinder.Service.CALDAV))
|
||||
|
||||
assertTrue(finder.providesService(server.url(PATH_CALDAV_AND_CARDDAV), DavResourceFinder.Service.CALDAV))
|
||||
assertTrue(finder.providesService(server.url(PATH_CALDAV_AND_CARDDAV), DavResourceFinder.Service.CARDDAV))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetCurrentUserPrincipal() {
|
||||
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_NO_DAV), DavResourceFinder.Service.CALDAV))
|
||||
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_NO_DAV), DavResourceFinder.Service.CARDDAV))
|
||||
|
||||
assertEquals(
|
||||
server.url(PATH_CALDAV + SUBPATH_PRINCIPAL),
|
||||
finder.getCurrentUserPrincipal(server.url(PATH_CALDAV), DavResourceFinder.Service.CALDAV)
|
||||
)
|
||||
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_CALDAV), DavResourceFinder.Service.CARDDAV))
|
||||
|
||||
assertEquals(
|
||||
server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL),
|
||||
finder.getCurrentUserPrincipal(server.url(PATH_CARDDAV), DavResourceFinder.Service.CARDDAV)
|
||||
)
|
||||
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_CARDDAV), DavResourceFinder.Service.CALDAV))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testQueryEmailAddress() {
|
||||
var info = ServiceInfo()
|
||||
assertArrayEquals(
|
||||
arrayOf("email1@example.com", "email2@example.com"),
|
||||
finder.queryEmailAddress(server.url(PATH_CALDAV + SUBPATH_PRINCIPAL)).toTypedArray()
|
||||
)
|
||||
assertTrue(finder.queryEmailAddress(server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL)).isEmpty())
|
||||
}
|
||||
|
||||
|
||||
// mock server
|
||||
|
||||
class TestDispatcher(
|
||||
private val logger: Logger
|
||||
): Dispatcher() {
|
||||
|
||||
override fun dispatch(request: RecordedRequest): MockResponse {
|
||||
if (!checkAuth(request)) {
|
||||
val authenticate = MockResponse().setResponseCode(401)
|
||||
authenticate.setHeader("WWW-Authenticate", "Basic realm=\"test\"")
|
||||
return authenticate
|
||||
}
|
||||
|
||||
val path = request.path!!
|
||||
|
||||
if (request.method.equals("OPTIONS", true)) {
|
||||
val dav = when {
|
||||
path.startsWith(PATH_CALDAV) -> "calendar-access"
|
||||
path.startsWith(PATH_CARDDAV) -> "addressbook"
|
||||
path.startsWith(PATH_CALDAV_AND_CARDDAV) -> "calendar-access, addressbook"
|
||||
else -> null
|
||||
}
|
||||
val response = MockResponse().setResponseCode(200)
|
||||
if (dav != null)
|
||||
response.addHeader("DAV", dav)
|
||||
return response
|
||||
} else if (request.method.equals("PROPFIND", true)) {
|
||||
val props: String?
|
||||
when (path) {
|
||||
PATH_CALDAV,
|
||||
PATH_CARDDAV ->
|
||||
props = "<current-user-principal><href>$path$SUBPATH_PRINCIPAL</href></current-user-principal>"
|
||||
|
||||
PATH_CARDDAV + SUBPATH_PRINCIPAL ->
|
||||
props = "<CARD:addressbook-home-set>" +
|
||||
" <href>$PATH_CARDDAV$SUBPATH_ADDRESSBOOK_HOMESET</href>" +
|
||||
"</CARD:addressbook-home-set>"
|
||||
|
||||
PATH_CARDDAV + SUBPATH_ADDRESSBOOK ->
|
||||
props = "<resourcetype>" +
|
||||
" <collection/>" +
|
||||
" <CARD:addressbook/>" +
|
||||
"</resourcetype>"
|
||||
|
||||
PATH_CALDAV + SUBPATH_PRINCIPAL ->
|
||||
props = "<CAL:calendar-user-address-set>" +
|
||||
" <href>urn:unknown-entry</href>" +
|
||||
" <href>mailto:email1@example.com</href>" +
|
||||
" <href>mailto:email2@example.com</href>" +
|
||||
"</CAL:calendar-user-address-set>"
|
||||
|
||||
else -> props = null
|
||||
}
|
||||
logger.info("Sending props: $props")
|
||||
return MockResponse()
|
||||
.setResponseCode(207)
|
||||
.setBody("<multistatus xmlns='DAV:' xmlns:CARD='urn:ietf:params:xml:ns:carddav' xmlns:CAL='urn:ietf:params:xml:ns:caldav'>" +
|
||||
"<response>" +
|
||||
" <href>${request.path}</href>" +
|
||||
" <propstat><prop>$props</prop></propstat>" +
|
||||
"</response>" +
|
||||
"</multistatus>")
|
||||
}
|
||||
|
||||
return MockResponse().setResponseCode(404)
|
||||
}
|
||||
|
||||
private fun checkAuth(rq: RecordedRequest) =
|
||||
rq.getHeader("Authorization") == "Basic bW9jazoxMjM0NQ=="
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.settings
|
||||
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.flow.toSet
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class SettingsManagerTest {
|
||||
|
||||
companion object {
|
||||
/** Use this setting to test SettingsManager methods. Will be removed after every test run. */
|
||||
const val SETTING_TEST = "test"
|
||||
}
|
||||
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
@get:Rule
|
||||
val instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
@Inject lateinit var settingsManager: SettingsManager
|
||||
|
||||
@Before
|
||||
fun inject() {
|
||||
hiltRule.inject()
|
||||
}
|
||||
|
||||
@After
|
||||
fun removeTestSetting() {
|
||||
settingsManager.remove(SETTING_TEST)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun test_containsKey_NotExisting() {
|
||||
assertFalse(settingsManager.containsKey("notExisting"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_containsKey_Existing() {
|
||||
// provided by DefaultsProvider
|
||||
assertEquals(Settings.PROXY_TYPE_SYSTEM, settingsManager.getInt(Settings.PROXY_TYPE))
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun test_observerFlow_initialValue() = runBlocking {
|
||||
var counter = 0
|
||||
val live = settingsManager.observerFlow {
|
||||
if (counter++ == 0)
|
||||
23
|
||||
else
|
||||
throw AssertionError("A second value was requested")
|
||||
}
|
||||
assertEquals(23, live.first())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_observerFlow_updatedValue() = runBlocking {
|
||||
var counter = 0
|
||||
val live = settingsManager.observerFlow {
|
||||
when (counter++) {
|
||||
0 -> {
|
||||
// update some setting so that we will be called a second time
|
||||
settingsManager.putBoolean(SETTING_TEST, true)
|
||||
// and emit initial value
|
||||
23
|
||||
}
|
||||
1 -> 42 // updated value
|
||||
else -> throw AssertionError()
|
||||
}
|
||||
}
|
||||
|
||||
val result = live.take(2).toSet()
|
||||
assertEquals(setOf(23, 42), result)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.sync
|
||||
|
||||
import at.bitfire.davdroid.db.SyncState
|
||||
import at.bitfire.davdroid.resource.LocalCollection
|
||||
|
||||
class LocalTestCollection(
|
||||
override val collectionUrl: String = "http://example.com/test/"
|
||||
): LocalCollection<LocalTestResource> {
|
||||
|
||||
override val tag = "LocalTestCollection"
|
||||
override val title = "Local Test Collection"
|
||||
|
||||
override var lastSyncState: SyncState? = null
|
||||
|
||||
val entries = mutableListOf<LocalTestResource>()
|
||||
|
||||
override val readOnly: Boolean
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override fun deleteCollection(): Boolean = true
|
||||
|
||||
override fun findDeleted() = entries.filter { it.deleted }
|
||||
override fun findDirty() = entries.filter { it.dirty }
|
||||
|
||||
override fun findByName(name: String) = entries.firstOrNull { it.fileName == name }
|
||||
|
||||
override fun markNotDirty(flags: Int): Int {
|
||||
var updated = 0
|
||||
for (dirty in findDirty()) {
|
||||
dirty.flags = flags
|
||||
updated++
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
override fun removeNotDirtyMarked(flags: Int): Int {
|
||||
val numBefore = entries.size
|
||||
entries.removeIf { !it.dirty && it.flags == flags }
|
||||
return numBefore - entries.size
|
||||
}
|
||||
|
||||
override fun forgetETags() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.sync
|
||||
|
||||
import at.bitfire.davdroid.resource.LocalResource
|
||||
|
||||
class LocalTestResource: LocalResource<Any> {
|
||||
|
||||
override val id: Long? = null
|
||||
override var fileName: String? = null
|
||||
override var eTag: String? = null
|
||||
override var scheduleTag: String? = null
|
||||
override var flags: Int = 0
|
||||
|
||||
var deleted = false
|
||||
var dirty = false
|
||||
|
||||
override fun prepareForUpload() = "generated-file.txt"
|
||||
|
||||
override fun clearDirty(fileName: String?, eTag: String?, scheduleTag: String?) {
|
||||
dirty = false
|
||||
if (fileName != null)
|
||||
this.fileName = fileName
|
||||
this.eTag = eTag
|
||||
this.scheduleTag = scheduleTag
|
||||
}
|
||||
|
||||
override fun updateFlags(flags: Int) {
|
||||
this.flags = flags
|
||||
}
|
||||
|
||||
override fun add() = throw NotImplementedError()
|
||||
override fun update(data: Any) = throw NotImplementedError()
|
||||
override fun delete() = throw NotImplementedError()
|
||||
override fun resetDeleted() = throw NotImplementedError()
|
||||
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.sync
|
||||
|
||||
import android.accounts.Account
|
||||
import android.content.Context
|
||||
import android.content.SyncResult
|
||||
import android.os.Bundle
|
||||
import android.provider.CalendarContract
|
||||
import android.util.Log
|
||||
import androidx.hilt.work.HiltWorkerFactory
|
||||
import androidx.work.Configuration
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.testing.WorkManagerTestInitHelper
|
||||
import at.bitfire.davdroid.repository.DavCollectionRepository
|
||||
import at.bitfire.davdroid.repository.DavServiceRepository
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import at.bitfire.davdroid.sync.account.TestAccountAuthenticator
|
||||
import at.bitfire.davdroid.sync.worker.SyncWorkerManager
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.mockk.Awaits
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkAll
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.Timeout
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.logging.Logger
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
@HiltAndroidTest
|
||||
class SyncAdapterServicesTest {
|
||||
|
||||
lateinit var account: Account
|
||||
|
||||
@Inject
|
||||
lateinit var accountSettingsFactory: AccountSettings.Factory
|
||||
|
||||
@Inject
|
||||
lateinit var collectionRepository: DavCollectionRepository
|
||||
|
||||
@Inject
|
||||
@ApplicationContext
|
||||
lateinit var context: Context
|
||||
|
||||
@Inject
|
||||
lateinit var logger: Logger
|
||||
|
||||
@Inject
|
||||
lateinit var serviceRepository: DavServiceRepository
|
||||
|
||||
@Inject
|
||||
lateinit var syncConditionsFactory: SyncConditions.Factory
|
||||
|
||||
@Inject
|
||||
lateinit var workerFactory: HiltWorkerFactory
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
// test methods should run quickly and not wait 60 seconds for a sync timeout or something like that
|
||||
@get:Rule
|
||||
val timeoutRule: Timeout = Timeout.seconds(5)
|
||||
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
hiltRule.inject()
|
||||
|
||||
account = TestAccountAuthenticator.create()
|
||||
|
||||
// Initialize WorkManager for instrumentation tests.
|
||||
val config = Configuration.Builder()
|
||||
.setMinimumLoggingLevel(Log.DEBUG)
|
||||
.setWorkerFactory(workerFactory)
|
||||
.build()
|
||||
WorkManagerTestInitHelper.initializeTestWorkManager(context, config)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
TestAccountAuthenticator.remove(account)
|
||||
unmockkAll()
|
||||
}
|
||||
|
||||
|
||||
private fun syncAdapter(
|
||||
syncWorkerManager: SyncWorkerManager
|
||||
): SyncAdapterService.SyncAdapter =
|
||||
SyncAdapterService.SyncAdapter(
|
||||
accountSettingsFactory = accountSettingsFactory,
|
||||
collectionRepository = collectionRepository,
|
||||
serviceRepository = serviceRepository,
|
||||
context = context,
|
||||
logger = logger,
|
||||
syncConditionsFactory = syncConditionsFactory,
|
||||
syncWorkerManager = syncWorkerManager
|
||||
)
|
||||
|
||||
|
||||
@Test
|
||||
fun testSyncAdapter_onPerformSync_cancellation() {
|
||||
val syncWorkerManager = mockk<SyncWorkerManager>()
|
||||
val syncAdapter = syncAdapter(syncWorkerManager = syncWorkerManager)
|
||||
val workManager = WorkManager.getInstance(context)
|
||||
|
||||
mockkObject(workManager) {
|
||||
// don't actually create a worker
|
||||
every { syncWorkerManager.enqueueOneTime(any(), any()) } returns "TheSyncWorker"
|
||||
|
||||
// assume worker takes a long time
|
||||
every { workManager.getWorkInfosForUniqueWorkFlow("TheSyncWorker") } just Awaits
|
||||
|
||||
runBlocking {
|
||||
val sync = launch {
|
||||
syncAdapter.onPerformSync(account, Bundle(), CalendarContract.AUTHORITY, mockk(), SyncResult())
|
||||
}
|
||||
|
||||
// simulate incoming cancellation from sync framework
|
||||
syncAdapter.onSyncCanceled()
|
||||
|
||||
// wait for sync to finish (should happen immediately)
|
||||
sync.join()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSyncAdapter_onPerformSync_returnsAfterTimeout() {
|
||||
val syncWorkerManager = mockk<SyncWorkerManager>()
|
||||
val syncAdapter = syncAdapter(syncWorkerManager = syncWorkerManager)
|
||||
val workManager = WorkManager.getInstance(context)
|
||||
|
||||
mockkObject(workManager) {
|
||||
// don't actually create a worker
|
||||
every { syncWorkerManager.enqueueOneTime(any(), any()) } returns "TheSyncWorker"
|
||||
|
||||
// assume worker takes a long time
|
||||
every { workManager.getWorkInfosForUniqueWorkFlow("TheSyncWorker") } just Awaits
|
||||
|
||||
mockkStatic("kotlinx.coroutines.TimeoutKt") { // mock global extension function
|
||||
// immediate timeout (instead of really waiting)
|
||||
coEvery { withTimeout(any<Long>(), any<suspend CoroutineScope.() -> Unit>()) } throws CancellationException("Simulated timeout")
|
||||
|
||||
syncAdapter.onPerformSync(account, Bundle(), CalendarContract.AUTHORITY, mockk(), SyncResult())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSyncAdapter_onPerformSync_runsInTime() {
|
||||
val syncWorkerManager = mockk<SyncWorkerManager>()
|
||||
val syncAdapter = syncAdapter(syncWorkerManager = syncWorkerManager)
|
||||
val workManager = WorkManager.getInstance(context)
|
||||
|
||||
mockkObject(workManager) {
|
||||
// don't actually create a worker
|
||||
every { syncWorkerManager.enqueueOneTime(any(), any()) } returns "TheSyncWorker"
|
||||
|
||||
// assume worker immediately returns with success
|
||||
val success = mockk<WorkInfo>()
|
||||
every { success.state } returns WorkInfo.State.SUCCEEDED
|
||||
every { workManager.getWorkInfosForUniqueWorkFlow("TheSyncWorker") } returns flow {
|
||||
emit(listOf(success))
|
||||
delay(60000) // keep the flow active
|
||||
}
|
||||
|
||||
// should just run
|
||||
syncAdapter.onPerformSync(account, Bundle(), CalendarContract.AUTHORITY, mockk(), SyncResult())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,533 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.sync
|
||||
|
||||
import android.accounts.Account
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.hilt.work.HiltWorkerFactory
|
||||
import androidx.work.Configuration
|
||||
import androidx.work.testing.WorkManagerTestInitHelper
|
||||
import at.bitfire.dav4jvm.PropStat
|
||||
import at.bitfire.dav4jvm.Response
|
||||
import at.bitfire.dav4jvm.Response.HrefRelation
|
||||
import at.bitfire.dav4jvm.property.webdav.GetETag
|
||||
import at.bitfire.davdroid.TestUtils.assertWithin
|
||||
import at.bitfire.davdroid.db.Collection
|
||||
import at.bitfire.davdroid.db.SyncState
|
||||
import at.bitfire.davdroid.network.HttpClient
|
||||
import at.bitfire.davdroid.repository.DavSyncStatsRepository
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import at.bitfire.davdroid.sync.account.TestAccountAuthenticator
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import okhttp3.Protocol
|
||||
import okhttp3.internal.http.StatusLine
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.time.Instant
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class SyncManagerTest {
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object SyncManagerTestModule {
|
||||
@Provides
|
||||
fun davSyncStatsRepository(): DavSyncStatsRepository = mockk<DavSyncStatsRepository>(relaxed = true)
|
||||
}
|
||||
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
lateinit var accountSettingsFactory: AccountSettings.Factory
|
||||
|
||||
@Inject
|
||||
@ApplicationContext
|
||||
lateinit var context: Context
|
||||
|
||||
@Inject
|
||||
lateinit var syncManagerFactory: TestSyncManager.Factory
|
||||
|
||||
@Inject
|
||||
lateinit var workerFactory: HiltWorkerFactory
|
||||
|
||||
lateinit var account: Account
|
||||
private val server = MockWebServer()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
hiltRule.inject()
|
||||
|
||||
// Initialize WorkManager for instrumentation tests.
|
||||
val config = Configuration.Builder()
|
||||
.setMinimumLoggingLevel(Log.DEBUG)
|
||||
.setWorkerFactory(workerFactory)
|
||||
.build()
|
||||
WorkManagerTestInitHelper.initializeTestWorkManager(context, config)
|
||||
|
||||
account = TestAccountAuthenticator.create()
|
||||
|
||||
server.start()
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
TestAccountAuthenticator.remove(account)
|
||||
|
||||
// clear annoying syncError notifications
|
||||
NotificationManagerCompat.from(context).cancelAll()
|
||||
|
||||
server.close()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testGetDelayUntil_defaultOnNull() {
|
||||
val now = Instant.now()
|
||||
val delayUntil = SyncManager.getDelayUntil(null).epochSecond
|
||||
val default = now.plusSeconds(SyncManager.DELAY_UNTIL_DEFAULT).epochSecond
|
||||
assertWithin(default, delayUntil, 5)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetDelayUntil_reducesToMax() {
|
||||
val now = Instant.now()
|
||||
val delayUntil = SyncManager.getDelayUntil(now.plusSeconds(10*24*60*60)).epochSecond
|
||||
val max = now.plusSeconds(SyncManager.DELAY_UNTIL_MAX).epochSecond
|
||||
assertWithin(max, delayUntil, 5)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetDelayUntil_increasesToMin() {
|
||||
val delayUntil = SyncManager.getDelayUntil(Instant.EPOCH).epochSecond
|
||||
val min = Instant.now().plusSeconds(SyncManager.DELAY_UNTIL_MIN).epochSecond
|
||||
assertWithin(min, delayUntil, 5)
|
||||
}
|
||||
|
||||
|
||||
private fun queryCapabilitiesResponse(cTag: String? = null): MockResponse {
|
||||
val body = StringBuilder()
|
||||
body.append(
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
|
||||
"<multistatus xmlns=\"DAV:\" xmlns:CALDAV=\"http://calendarserver.org/ns/\">\n" +
|
||||
" <response>\n" +
|
||||
" <href>/</href>\n" +
|
||||
" <propstat>\n" +
|
||||
" <prop>\n"
|
||||
)
|
||||
if (cTag != null)
|
||||
body.append("<CALDAV:getctag>$cTag</CALDAV:getctag>\n")
|
||||
body.append(
|
||||
" </prop>\n" +
|
||||
" </propstat>\n" +
|
||||
" </response>\n" +
|
||||
"</multistatus>"
|
||||
)
|
||||
return MockResponse()
|
||||
.setResponseCode(207)
|
||||
.setHeader("Content-Type", "text/xml")
|
||||
.setBody(body.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPerformSync_503RetryAfter_DelaySeconds() {
|
||||
server.enqueue(MockResponse()
|
||||
.setResponseCode(503)
|
||||
.setHeader("Retry-After", "60")) // 60 seconds
|
||||
|
||||
val result = SyncResult()
|
||||
val syncManager = syncManager(LocalTestCollection(), result)
|
||||
syncManager.performSync()
|
||||
|
||||
val expected = Instant.now()
|
||||
.plusSeconds(60)
|
||||
.toEpochMilli()
|
||||
// 5 sec tolerance for test
|
||||
assertWithin(expected, result.delayUntil*1000, 5000)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPerformSync_FirstSync_Empty() {
|
||||
val collection = LocalTestCollection() /* no last known ctag */
|
||||
server.enqueue(queryCapabilitiesResponse())
|
||||
|
||||
val syncManager = syncManager(collection)
|
||||
syncManager.performSync()
|
||||
|
||||
assertFalse(syncManager.didGenerateUpload)
|
||||
assertTrue(syncManager.didListAllRemote)
|
||||
assertFalse(syncManager.didDownloadRemote)
|
||||
assertFalse(syncManager.syncResult.hasError())
|
||||
assertTrue(collection.entries.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPerformSync_UploadNewMember_ETagOnPut() {
|
||||
val collection = LocalTestCollection().apply {
|
||||
lastSyncState = SyncState(SyncState.Type.CTAG, "old-ctag")
|
||||
entries += LocalTestResource().apply {
|
||||
dirty = true
|
||||
}
|
||||
}
|
||||
server.enqueue(queryCapabilitiesResponse("ctag1"))
|
||||
|
||||
// PUT -> 204 No Content
|
||||
server.enqueue(MockResponse()
|
||||
.setResponseCode(204)
|
||||
.setHeader("ETag", "etag-from-put"))
|
||||
|
||||
// modifications sent, so DAVx5 will query CTag again
|
||||
server.enqueue(queryCapabilitiesResponse("ctag2"))
|
||||
|
||||
val syncManager = syncManager(collection).apply {
|
||||
listAllRemoteResult = listOf(
|
||||
Pair(Response(
|
||||
server.url("/"),
|
||||
server.url("/generated-file.txt"),
|
||||
null,
|
||||
listOf(PropStat(
|
||||
listOf(
|
||||
GetETag("\"etag-from-put\"")
|
||||
),
|
||||
StatusLine(Protocol.HTTP_1_1, 200, "OK")
|
||||
)
|
||||
)), HrefRelation.MEMBER)
|
||||
)
|
||||
}
|
||||
syncManager.performSync()
|
||||
|
||||
assertTrue(syncManager.didGenerateUpload)
|
||||
assertTrue(syncManager.didListAllRemote)
|
||||
assertFalse(syncManager.didDownloadRemote)
|
||||
assertFalse(syncManager.syncResult.hasError())
|
||||
assertEquals(1, collection.entries.size)
|
||||
assertEquals("etag-from-put", collection.entries.first().eTag)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPerformSync_UploadModifiedMember_ETagOnPut() {
|
||||
val collection = LocalTestCollection().apply {
|
||||
lastSyncState = SyncState(SyncState.Type.CTAG, "old-ctag")
|
||||
entries += LocalTestResource().apply {
|
||||
fileName = "existing-file.txt"
|
||||
eTag = "old-etag-like-on-server"
|
||||
dirty = true
|
||||
}
|
||||
}
|
||||
server.enqueue(queryCapabilitiesResponse("ctag1"))
|
||||
|
||||
// PUT -> 204 No Content
|
||||
server.enqueue(MockResponse()
|
||||
.setResponseCode(204)
|
||||
.addHeader("ETag", "etag-from-put"))
|
||||
|
||||
// modifications sent, so DAVx5 will query CTag again
|
||||
server.enqueue(queryCapabilitiesResponse("ctag2"))
|
||||
|
||||
val syncManager = syncManager(collection).apply {
|
||||
listAllRemoteResult = listOf(
|
||||
Pair(Response(
|
||||
server.url("/"),
|
||||
server.url("/existing-file.txt"),
|
||||
null,
|
||||
listOf(PropStat(
|
||||
listOf(
|
||||
GetETag("etag-from-put")
|
||||
),
|
||||
StatusLine(Protocol.HTTP_1_1, 200, "OK")
|
||||
)
|
||||
)), HrefRelation.MEMBER)
|
||||
)
|
||||
|
||||
assertDownloadRemote = mapOf(Pair(server.url("/existing-file.txt"), "etag-from-put"))
|
||||
}
|
||||
syncManager.performSync()
|
||||
|
||||
assertTrue(syncManager.didGenerateUpload)
|
||||
assertTrue(syncManager.didListAllRemote)
|
||||
assertFalse(syncManager.didDownloadRemote)
|
||||
assertFalse(syncManager.syncResult.hasError())
|
||||
assertEquals(1, collection.entries.size)
|
||||
assertEquals("etag-from-put", collection.entries.first().eTag)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPerformSync_UploadModifiedMember_NoETagOnPut() {
|
||||
val collection = LocalTestCollection().apply {
|
||||
lastSyncState = SyncState(SyncState.Type.CTAG, "old-ctag")
|
||||
entries += LocalTestResource().apply {
|
||||
fileName = "existing-file.txt"
|
||||
eTag = "old-etag-like-on-server"
|
||||
dirty = true
|
||||
}
|
||||
}
|
||||
server.enqueue(queryCapabilitiesResponse("ctag1"))
|
||||
|
||||
// PUT -> 204 No Content
|
||||
server.enqueue(MockResponse().setResponseCode(204))
|
||||
|
||||
// modifications sent, so DAVx5 will query CTag again
|
||||
server.enqueue(queryCapabilitiesResponse("ctag2"))
|
||||
|
||||
val syncManager = syncManager(collection).apply {
|
||||
listAllRemoteResult = listOf(
|
||||
Pair(Response(
|
||||
server.url("/"),
|
||||
server.url("/existing-file.txt"),
|
||||
null,
|
||||
listOf(PropStat(
|
||||
listOf(
|
||||
GetETag("etag-from-propfind")
|
||||
),
|
||||
StatusLine(Protocol.HTTP_1_1, 200, "OK")
|
||||
)
|
||||
)), HrefRelation.MEMBER)
|
||||
)
|
||||
|
||||
assertDownloadRemote = mapOf(Pair(server.url("/existing-file.txt"), "etag-from-propfind"))
|
||||
}
|
||||
syncManager.performSync()
|
||||
|
||||
assertTrue(syncManager.didGenerateUpload)
|
||||
assertTrue(syncManager.didListAllRemote)
|
||||
assertTrue(syncManager.didDownloadRemote)
|
||||
assertFalse(syncManager.syncResult.hasError())
|
||||
assertEquals(1, collection.entries.size)
|
||||
assertEquals("etag-from-propfind", collection.entries.first().eTag)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPerformSync_UploadModifiedMember_412PreconditionFailed() {
|
||||
val collection = LocalTestCollection().apply {
|
||||
lastSyncState = SyncState(SyncState.Type.CTAG, "old-ctag")
|
||||
entries += LocalTestResource().apply {
|
||||
fileName = "existing-file.txt"
|
||||
eTag = "etag-that-has-been-changed-on-server-in-the-meanwhile"
|
||||
dirty = true
|
||||
}
|
||||
}
|
||||
server.enqueue(queryCapabilitiesResponse("ctag1"))
|
||||
|
||||
// PUT -> 412 Precondition Failed
|
||||
server.enqueue(MockResponse()
|
||||
.setResponseCode(412))
|
||||
|
||||
// modifications sent, so DAVx5 will query CTag again
|
||||
server.enqueue(queryCapabilitiesResponse("ctag1"))
|
||||
|
||||
val syncManager = syncManager(collection).apply {
|
||||
listAllRemoteResult = listOf(
|
||||
Pair(Response(
|
||||
server.url("/"),
|
||||
server.url("/existing-file.txt"),
|
||||
null,
|
||||
listOf(PropStat(
|
||||
listOf(
|
||||
GetETag("changed-etag-from-server")
|
||||
),
|
||||
StatusLine(Protocol.HTTP_1_1, 200, "OK")
|
||||
)
|
||||
)), HrefRelation.MEMBER)
|
||||
)
|
||||
|
||||
assertDownloadRemote = mapOf(Pair(server.url("/existing-file.txt"), "changed-etag-from-server"))
|
||||
}
|
||||
syncManager.performSync()
|
||||
|
||||
assertTrue(syncManager.didGenerateUpload)
|
||||
assertTrue(syncManager.didListAllRemote)
|
||||
assertTrue(syncManager.didDownloadRemote)
|
||||
assertFalse(syncManager.syncResult.hasError())
|
||||
assertEquals(1, collection.entries.size)
|
||||
assertEquals("changed-etag-from-server", collection.entries.first().eTag)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPerformSync_NoopOnMemberWithSameETag() {
|
||||
val collection = LocalTestCollection().apply {
|
||||
lastSyncState = SyncState(SyncState.Type.CTAG, "ctag1")
|
||||
entries += LocalTestResource().apply {
|
||||
fileName = "downloaded-member.txt"
|
||||
eTag = "MemberETag1"
|
||||
}
|
||||
}
|
||||
server.enqueue(queryCapabilitiesResponse("ctag2"))
|
||||
|
||||
val syncManager = syncManager(collection).apply {
|
||||
listAllRemoteResult = listOf(
|
||||
Pair(Response(
|
||||
server.url("/"),
|
||||
server.url("/downloaded-member.txt"),
|
||||
null,
|
||||
listOf(PropStat(
|
||||
listOf(
|
||||
GetETag("\"MemberETag1\"")
|
||||
),
|
||||
StatusLine(Protocol.HTTP_1_1, 200, "OK")
|
||||
)
|
||||
)), HrefRelation.MEMBER)
|
||||
)
|
||||
|
||||
}
|
||||
syncManager.performSync()
|
||||
|
||||
assertFalse(syncManager.didGenerateUpload)
|
||||
assertTrue(syncManager.didListAllRemote)
|
||||
assertFalse(syncManager.didDownloadRemote)
|
||||
assertFalse(syncManager.syncResult.hasError())
|
||||
assertEquals(1, collection.entries.size)
|
||||
assertEquals("MemberETag1", collection.entries.first().eTag)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPerformSync_DownloadNewMember() {
|
||||
val collection = LocalTestCollection().apply {
|
||||
lastSyncState = SyncState(SyncState.Type.CTAG, "old-ctag")
|
||||
}
|
||||
server.enqueue(queryCapabilitiesResponse(cTag = "new-ctag"))
|
||||
|
||||
val syncManager = syncManager(collection).apply {
|
||||
listAllRemoteResult = listOf(
|
||||
Pair(Response(
|
||||
server.url("/"),
|
||||
server.url("/new-member.txt"),
|
||||
null,
|
||||
listOf(PropStat(
|
||||
listOf(
|
||||
GetETag("\"NewMemberETag1\"")
|
||||
),
|
||||
StatusLine(Protocol.HTTP_1_1, 200, "OK")
|
||||
)
|
||||
)), HrefRelation.MEMBER)
|
||||
)
|
||||
|
||||
assertDownloadRemote = mapOf(Pair(server.url("/new-member.txt"), "NewMemberETag1"))
|
||||
}
|
||||
syncManager.performSync()
|
||||
|
||||
assertFalse(syncManager.didGenerateUpload)
|
||||
assertTrue(syncManager.didListAllRemote)
|
||||
assertTrue(syncManager.didDownloadRemote)
|
||||
assertFalse(syncManager.syncResult.hasError())
|
||||
assertEquals(1, collection.entries.size)
|
||||
assertEquals("NewMemberETag1", collection.entries.first().eTag)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPerformSync_DownloadUpdatedMember() {
|
||||
val collection = LocalTestCollection().apply {
|
||||
lastSyncState = SyncState(SyncState.Type.CTAG, "old-ctag")
|
||||
entries += LocalTestResource().apply {
|
||||
fileName = "downloaded-member.txt"
|
||||
eTag = "MemberETag1"
|
||||
}
|
||||
}
|
||||
server.enqueue(queryCapabilitiesResponse(cTag = "new-ctag"))
|
||||
|
||||
val syncManager = syncManager(collection).apply {
|
||||
listAllRemoteResult = listOf(
|
||||
Pair(Response(
|
||||
server.url("/"),
|
||||
server.url("/downloaded-member.txt"),
|
||||
null,
|
||||
listOf(PropStat(
|
||||
listOf(
|
||||
GetETag("\"MemberETag2\"")
|
||||
),
|
||||
StatusLine(Protocol.HTTP_1_1, 200, "OK")
|
||||
)
|
||||
)), HrefRelation.MEMBER)
|
||||
)
|
||||
|
||||
assertDownloadRemote = mapOf(Pair(server.url("/downloaded-member.txt"), "MemberETag2"))
|
||||
}
|
||||
syncManager.performSync()
|
||||
|
||||
assertFalse(syncManager.didGenerateUpload)
|
||||
assertTrue(syncManager.didListAllRemote)
|
||||
assertTrue(syncManager.didDownloadRemote)
|
||||
assertFalse(syncManager.syncResult.hasError())
|
||||
assertEquals(1, collection.entries.size)
|
||||
assertEquals("MemberETag2", collection.entries.first().eTag)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPerformSync_RemoveVanishedMember() {
|
||||
val collection = LocalTestCollection().apply {
|
||||
lastSyncState = SyncState(SyncState.Type.CTAG, "old-ctag")
|
||||
entries += LocalTestResource().apply {
|
||||
fileName = "downloaded-member.txt"
|
||||
}
|
||||
}
|
||||
server.enqueue(queryCapabilitiesResponse(cTag = "new-ctag"))
|
||||
|
||||
val syncManager = syncManager(collection)
|
||||
syncManager.performSync()
|
||||
|
||||
assertFalse(syncManager.didGenerateUpload)
|
||||
assertTrue(syncManager.didListAllRemote)
|
||||
assertFalse(syncManager.didDownloadRemote)
|
||||
assertFalse(syncManager.syncResult.hasError())
|
||||
assertTrue(collection.entries.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPerformSync_CTagDidntChange() {
|
||||
val collection = LocalTestCollection().apply {
|
||||
lastSyncState = SyncState(SyncState.Type.CTAG, "ctag1")
|
||||
}
|
||||
server.enqueue(queryCapabilitiesResponse("ctag1"))
|
||||
|
||||
val syncManager = syncManager(collection)
|
||||
syncManager.performSync()
|
||||
|
||||
assertFalse(syncManager.didGenerateUpload)
|
||||
assertFalse(syncManager.didListAllRemote)
|
||||
assertFalse(syncManager.didDownloadRemote)
|
||||
assertFalse(syncManager.syncResult.hasError())
|
||||
assertTrue(collection.entries.isEmpty())
|
||||
}
|
||||
|
||||
|
||||
// helpers
|
||||
|
||||
private fun syncManager(
|
||||
localCollection: LocalTestCollection,
|
||||
syncResult: SyncResult = SyncResult(),
|
||||
collection: Collection = mockk<Collection>() {
|
||||
every { id } returns 1
|
||||
every { url } returns server.url("/")
|
||||
}
|
||||
) = syncManagerFactory.create(
|
||||
account,
|
||||
accountSettingsFactory.create(account),
|
||||
arrayOf(),
|
||||
"TestAuthority",
|
||||
HttpClient.Builder(context).build(),
|
||||
syncResult,
|
||||
localCollection,
|
||||
collection
|
||||
)
|
||||
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.sync
|
||||
|
||||
import android.accounts.Account
|
||||
import android.content.ContentProviderClient
|
||||
import at.bitfire.davdroid.db.Collection
|
||||
import at.bitfire.davdroid.sync.account.TestAccountAuthenticator
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import io.mockk.verify
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class SyncerTest {
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
lateinit var testSyncer: TestSyncer.Factory
|
||||
|
||||
lateinit var account: Account
|
||||
|
||||
private lateinit var syncer: TestSyncer
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
hiltRule.inject()
|
||||
|
||||
account = TestAccountAuthenticator.create()
|
||||
syncer = spyk(testSyncer.create(account, emptyArray(), SyncResult()))
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
TestAccountAuthenticator.remove(account)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testSync_prepare_fails() {
|
||||
val provider = mockk<ContentProviderClient>()
|
||||
every { syncer.prepare(provider) } returns false
|
||||
every { syncer.getSyncEnabledCollections() } returns emptyMap()
|
||||
|
||||
// Should stop the sync after prepare returns false
|
||||
syncer.sync(provider)
|
||||
verify(exactly = 1) { syncer.prepare(provider) }
|
||||
verify(exactly = 0) { syncer.getSyncEnabledCollections() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSync_prepare_succeeds() {
|
||||
val provider = mockk<ContentProviderClient>()
|
||||
every { syncer.prepare(provider) } returns true
|
||||
every { syncer.getSyncEnabledCollections() } returns emptyMap()
|
||||
|
||||
// Should continue the sync after prepare returns true
|
||||
syncer.sync(provider)
|
||||
verify(exactly = 1) { syncer.prepare(provider) }
|
||||
verify(exactly = 1) { syncer.getSyncEnabledCollections() }
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testUpdateCollections_deletesCollection() {
|
||||
val localCollection = mockk<LocalTestCollection>()
|
||||
every { localCollection.collectionUrl } returns "http://delete.the/collection"
|
||||
every { localCollection.deleteCollection() } returns true
|
||||
every { localCollection.title } returns "Collection to be deleted locally"
|
||||
|
||||
// Should delete the localCollection if dbCollection (remote) does not exist
|
||||
val localCollections = mutableListOf(localCollection)
|
||||
val result = syncer.updateCollections(mockk(), localCollections, emptyMap())
|
||||
verify(exactly = 1) { localCollection.deleteCollection() }
|
||||
|
||||
// Updated local collection list should be empty
|
||||
assertTrue(result.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUpdateCollections_updatesCollection() {
|
||||
val localCollection = mockk<LocalTestCollection>()
|
||||
val dbCollection = mockk<Collection>()
|
||||
val dbCollections = mapOf("http://update.the/collection".toHttpUrl() to dbCollection)
|
||||
every { dbCollection.url } returns "http://update.the/collection".toHttpUrl()
|
||||
every { localCollection.collectionUrl } returns "http://update.the/collection"
|
||||
every { localCollection.title } returns "The Local Collection"
|
||||
|
||||
// Should update the localCollection if it exists
|
||||
val result = syncer.updateCollections(mockk(), listOf(localCollection), dbCollections)
|
||||
verify(exactly = 1) { syncer.update(localCollection, dbCollection) }
|
||||
|
||||
// Updated local collection list should be same as input
|
||||
assertArrayEquals(arrayOf(localCollection), result.toTypedArray())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUpdateCollections_findsNewCollection() {
|
||||
val dbCollection = mockk<Collection>()
|
||||
every { dbCollection.url } returns "http://newly.found/collection".toHttpUrl()
|
||||
val dbCollections = mapOf(dbCollection.url to dbCollection)
|
||||
|
||||
// Should return the new collection, because it was not updated
|
||||
val result = syncer.updateCollections(mockk(), emptyList(), dbCollections)
|
||||
|
||||
// Updated local collection list contain new entry
|
||||
assertEquals(1, result.size)
|
||||
assertEquals(dbCollection.url.toString(), result[0].collectionUrl)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testCreateLocalCollections() {
|
||||
val provider = mockk<ContentProviderClient>()
|
||||
val localCollection = mockk<LocalTestCollection>()
|
||||
val dbCollection = mockk<Collection>()
|
||||
every { syncer.create(provider, dbCollection) } returns localCollection
|
||||
every { dbCollection.url } returns "http://newly.found/collection".toHttpUrl()
|
||||
|
||||
// Should return list of newly created local collections
|
||||
val result = syncer.createLocalCollections(provider, listOf(dbCollection))
|
||||
assertEquals(listOf(localCollection), result)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testSyncCollectionContents() {
|
||||
val provider = mockk<ContentProviderClient>()
|
||||
val dbCollection1 = mockk<Collection>()
|
||||
val dbCollection2 = mockk<Collection>()
|
||||
val dbCollections = mapOf(
|
||||
"http://newly.found/collection1".toHttpUrl() to dbCollection1,
|
||||
"http://newly.found/collection2".toHttpUrl() to dbCollection2
|
||||
)
|
||||
val localCollection1 = mockk<LocalTestCollection>()
|
||||
val localCollection2 = mockk<LocalTestCollection>()
|
||||
val localCollections = listOf(localCollection1, localCollection2)
|
||||
every { localCollection1.collectionUrl } returns "http://newly.found/collection1"
|
||||
every { localCollection2.collectionUrl } returns "http://newly.found/collection2"
|
||||
|
||||
// Should call the collection content sync on both collections
|
||||
syncer.syncCollectionContents(provider, localCollections, dbCollections)
|
||||
verify(exactly = 1) { syncer.syncCollection(provider, localCollection1, dbCollection1) }
|
||||
verify(exactly = 1) { syncer.syncCollection(provider, localCollection2, dbCollection2) }
|
||||
}
|
||||
|
||||
|
||||
// Test helpers
|
||||
|
||||
class TestSyncer @AssistedInject constructor(
|
||||
@Assisted account: Account,
|
||||
@Assisted extras: Array<String>,
|
||||
@Assisted syncResult: SyncResult
|
||||
) : Syncer<LocalTestCollection>(account, extras, syncResult) {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(account: Account, extras: Array<String>, syncResult: SyncResult): TestSyncer
|
||||
}
|
||||
|
||||
override val authority: String
|
||||
get() = ""
|
||||
override val serviceType: String
|
||||
get() = ""
|
||||
|
||||
override fun prepare(provider: ContentProviderClient): Boolean =
|
||||
true
|
||||
|
||||
override fun getLocalCollections(provider: ContentProviderClient): List<LocalTestCollection> =
|
||||
emptyList()
|
||||
|
||||
override fun getDbSyncCollections(serviceId: Long): List<Collection> =
|
||||
emptyList()
|
||||
|
||||
override fun create(provider: ContentProviderClient, remoteCollection: Collection): LocalTestCollection =
|
||||
LocalTestCollection(remoteCollection.url.toString())
|
||||
|
||||
override fun syncCollection(
|
||||
provider: ContentProviderClient,
|
||||
localCollection: LocalTestCollection,
|
||||
remoteCollection: Collection
|
||||
) {}
|
||||
|
||||
override fun update(localCollection: LocalTestCollection, remoteCollection: Collection) {}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.sync
|
||||
|
||||
import android.accounts.Account
|
||||
import at.bitfire.dav4jvm.DavCollection
|
||||
import at.bitfire.dav4jvm.MultiResponseCallback
|
||||
import at.bitfire.dav4jvm.Response
|
||||
import at.bitfire.dav4jvm.property.caldav.GetCTag
|
||||
import at.bitfire.davdroid.db.Collection
|
||||
import at.bitfire.davdroid.db.SyncState
|
||||
import at.bitfire.davdroid.network.HttpClient
|
||||
import at.bitfire.davdroid.resource.LocalResource
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import at.bitfire.davdroid.util.DavUtils.lastSegment
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import org.junit.Assert.assertEquals
|
||||
|
||||
class TestSyncManager @AssistedInject constructor(
|
||||
@Assisted account: Account,
|
||||
@Assisted accountSettings: AccountSettings,
|
||||
@Assisted extras: Array<String>,
|
||||
@Assisted authority: String,
|
||||
@Assisted httpClient: HttpClient,
|
||||
@Assisted syncResult: SyncResult,
|
||||
@Assisted localCollection: LocalTestCollection,
|
||||
@Assisted collection: Collection
|
||||
): SyncManager<LocalTestResource, LocalTestCollection, DavCollection>(
|
||||
account,
|
||||
accountSettings,
|
||||
httpClient,
|
||||
extras,
|
||||
authority,
|
||||
syncResult,
|
||||
localCollection,
|
||||
collection
|
||||
) {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(
|
||||
account: Account,
|
||||
accountSettings: AccountSettings,
|
||||
extras: Array<String>,
|
||||
authority: String,
|
||||
httpClient: HttpClient,
|
||||
syncResult: SyncResult,
|
||||
localCollection: LocalTestCollection,
|
||||
collection: Collection
|
||||
): TestSyncManager
|
||||
}
|
||||
|
||||
override fun prepare(): Boolean {
|
||||
davCollection = DavCollection(httpClient.okHttpClient, collection.url)
|
||||
return true
|
||||
}
|
||||
|
||||
var didQueryCapabilities = false
|
||||
override fun queryCapabilities(): SyncState? {
|
||||
if (didQueryCapabilities)
|
||||
throw IllegalStateException("queryCapabilities() must not be called twice")
|
||||
didQueryCapabilities = true
|
||||
|
||||
var cTag: SyncState? = null
|
||||
davCollection.propfind(0, GetCTag.NAME) { response, rel ->
|
||||
if (rel == Response.HrefRelation.SELF)
|
||||
response[GetCTag::class.java]?.cTag?.let {
|
||||
cTag = SyncState(SyncState.Type.CTAG, it)
|
||||
}
|
||||
}
|
||||
|
||||
return cTag
|
||||
}
|
||||
|
||||
var didGenerateUpload = false
|
||||
override fun generateUpload(resource: LocalTestResource): RequestBody {
|
||||
didGenerateUpload = true
|
||||
return resource.toString().toRequestBody()
|
||||
}
|
||||
|
||||
override fun syncAlgorithm() = SyncAlgorithm.PROPFIND_REPORT
|
||||
|
||||
var listAllRemoteResult = emptyList<Pair<Response, Response.HrefRelation>>()
|
||||
var didListAllRemote = false
|
||||
override fun listAllRemote(callback: MultiResponseCallback) {
|
||||
if (didListAllRemote)
|
||||
throw IllegalStateException("listAllRemote() must not be called twice")
|
||||
didListAllRemote = true
|
||||
for (result in listAllRemoteResult)
|
||||
callback.onResponse(result.first, result.second)
|
||||
}
|
||||
|
||||
var assertDownloadRemote = emptyMap<HttpUrl, String>()
|
||||
var didDownloadRemote = false
|
||||
override fun downloadRemote(bunch: List<HttpUrl>) {
|
||||
didDownloadRemote = true
|
||||
assertEquals(assertDownloadRemote.keys.toList(), bunch)
|
||||
|
||||
for ((url, eTag) in assertDownloadRemote) {
|
||||
val fileName = url.lastSegment
|
||||
var localEntry = localCollection.entries.firstOrNull { it.fileName == fileName }
|
||||
if (localEntry == null) {
|
||||
val newEntry = LocalTestResource().also {
|
||||
it.fileName = fileName
|
||||
}
|
||||
localCollection.entries += newEntry
|
||||
localEntry = newEntry
|
||||
}
|
||||
localEntry.eTag = eTag
|
||||
localEntry.flags = LocalResource.FLAG_REMOTELY_PRESENT
|
||||
}
|
||||
}
|
||||
|
||||
override fun postProcess() {
|
||||
}
|
||||
|
||||
override fun notifyInvalidResourceTitle() =
|
||||
throw NotImplementedError()
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.sync.account
|
||||
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountManager
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import at.bitfire.davdroid.settings.SettingsManager
|
||||
import at.bitfire.davdroid.test.R
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class AccountUtilsTest {
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
@ApplicationContext
|
||||
lateinit var context: Context
|
||||
val testContext = InstrumentationRegistry.getInstrumentation().context
|
||||
|
||||
@Inject
|
||||
lateinit var settingsManager: SettingsManager
|
||||
|
||||
val account = Account(
|
||||
"AccountUtilsTest",
|
||||
testContext.getString(R.string.account_type_test)
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
hiltRule.inject()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testCreateAccount() {
|
||||
val userData = Bundle(2)
|
||||
userData.putString("int", "1")
|
||||
userData.putString("string", "abc/\"-")
|
||||
|
||||
val manager = AccountManager.get(context)
|
||||
try {
|
||||
assertTrue(SystemAccountUtils.createAccount(context, account, userData))
|
||||
|
||||
// validate user data
|
||||
assertEquals("1", manager.getUserData(account, "int"))
|
||||
assertEquals("abc/\"-", manager.getUserData(account, "string"))
|
||||
} finally {
|
||||
assertTrue(manager.removeAccountExplicitly(account))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
package at.bitfire.davdroid.sync.account
|
||||
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountManager
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.hilt.work.HiltWorkerFactory
|
||||
import androidx.work.Configuration
|
||||
import androidx.work.WorkerFactory
|
||||
import androidx.work.WorkerParameters
|
||||
import androidx.work.testing.TestListenableWorkerBuilder
|
||||
import androidx.work.testing.WorkManagerTestInitHelper
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.db.AppDatabase
|
||||
import at.bitfire.davdroid.db.Collection
|
||||
import at.bitfire.davdroid.db.Service
|
||||
import at.bitfire.davdroid.resource.LocalAddressBook.Companion.USER_DATA_COLLECTION_ID
|
||||
import at.bitfire.davdroid.settings.SettingsManager
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class AccountsCleanupWorkerTest {
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
lateinit var accountsCleanupWorkerFactory: AccountsCleanupWorker.Factory
|
||||
|
||||
@Inject
|
||||
@ApplicationContext
|
||||
lateinit var context: Context
|
||||
|
||||
@Inject
|
||||
lateinit var db: AppDatabase
|
||||
|
||||
@Inject
|
||||
lateinit var settingsManager: SettingsManager
|
||||
|
||||
@Inject
|
||||
lateinit var workerFactory: HiltWorkerFactory
|
||||
|
||||
lateinit var accountManager: AccountManager
|
||||
lateinit var addressBookAccountType: String
|
||||
lateinit var addressBookAccount: Account
|
||||
lateinit var service: Service
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
hiltRule.inject()
|
||||
|
||||
service = createTestService(Service.TYPE_CARDDAV)
|
||||
|
||||
// Prepare test account
|
||||
accountManager = AccountManager.get(context)
|
||||
addressBookAccountType = context.getString(R.string.account_type_address_book)
|
||||
addressBookAccount = Account(
|
||||
"Fancy address book account",
|
||||
addressBookAccountType
|
||||
)
|
||||
|
||||
// Initialize WorkManager for instrumentation tests.
|
||||
val config = Configuration.Builder()
|
||||
.setMinimumLoggingLevel(Log.DEBUG)
|
||||
.setWorkerFactory(workerFactory)
|
||||
.build()
|
||||
WorkManagerTestInitHelper.initializeTestWorkManager(context, config)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
// Remove the account here in any case; Nice to have when the test fails
|
||||
accountManager.removeAccountExplicitly(addressBookAccount)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testDeleteOrphanedAddressBookAccounts_deletesAddressBookAccountWithoutCollection() {
|
||||
// Create address book account without corresponding collection
|
||||
assertTrue(accountManager.addAccountExplicitly(addressBookAccount, null, null))
|
||||
|
||||
val addressBookAccounts = accountManager.getAccountsByType(addressBookAccountType)
|
||||
assertEquals(addressBookAccount, addressBookAccounts.firstOrNull())
|
||||
|
||||
// Create worker and run the method
|
||||
val worker = TestListenableWorkerBuilder<AccountsCleanupWorker>(context)
|
||||
.setWorkerFactory(object: WorkerFactory() {
|
||||
override fun createWorker(appContext: Context, workerClassName: String, workerParameters: WorkerParameters) =
|
||||
accountsCleanupWorkerFactory.create(appContext, workerParameters)
|
||||
})
|
||||
.build()
|
||||
worker.deleteOrphanedAddressBookAccounts(addressBookAccounts)
|
||||
|
||||
// Verify account was deleted
|
||||
assertTrue(accountManager.getAccountsByType(addressBookAccountType).isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDeleteOrphanedAddressBookAccounts_leavesAddressBookAccountWithCollection() {
|
||||
// Create address book account _with_ corresponding collection and verify
|
||||
val randomCollectionId = 12345L
|
||||
val userData = Bundle(1).apply {
|
||||
putString(USER_DATA_COLLECTION_ID, "$randomCollectionId")
|
||||
}
|
||||
assertTrue(accountManager.addAccountExplicitly(addressBookAccount, null, userData))
|
||||
|
||||
val addressBookAccounts = accountManager.getAccountsByType(addressBookAccountType)
|
||||
assertEquals(randomCollectionId, accountManager.getUserData(addressBookAccount, USER_DATA_COLLECTION_ID).toLong())
|
||||
|
||||
// Create the collection
|
||||
val collectionDao = db.collectionDao()
|
||||
collectionDao.insert(Collection(
|
||||
randomCollectionId,
|
||||
serviceId = service.id,
|
||||
type = Collection.TYPE_ADDRESSBOOK,
|
||||
url = "http://www.example.com/yay.php".toHttpUrl()
|
||||
))
|
||||
|
||||
// Create worker and run the method
|
||||
val worker = TestListenableWorkerBuilder<AccountsCleanupWorker>(context)
|
||||
.setWorkerFactory(object: WorkerFactory() {
|
||||
override fun createWorker(appContext: Context, workerClassName: String, workerParameters: WorkerParameters) =
|
||||
accountsCleanupWorkerFactory.create(appContext, workerParameters)
|
||||
})
|
||||
.build()
|
||||
worker.deleteOrphanedAddressBookAccounts(addressBookAccounts)
|
||||
|
||||
// Verify account was _not_ deleted
|
||||
assertEquals(addressBookAccount, addressBookAccounts.firstOrNull())
|
||||
}
|
||||
|
||||
|
||||
// helpers
|
||||
|
||||
private fun createTestService(serviceType: String): Service {
|
||||
val service = Service(id=0, accountName="test", type=serviceType, principal = null)
|
||||
val serviceId = db.serviceDao().insertOrReplace(service)
|
||||
return db.serviceDao().get(serviceId)!!
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
**************************************************************************************************/
|
||||
package at.bitfire.davdroid.sync.account
|
||||
|
||||
import android.accounts.AbstractAccountAuthenticator
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountAuthenticatorResponse
|
||||
import android.accounts.AccountManager
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import at.bitfire.davdroid.test.R
|
||||
import org.junit.Assert.assertTrue
|
||||
|
||||
/**
|
||||
* Handles the test account type, which has no sync adapters and side effects that run unintentionally.
|
||||
*
|
||||
* Usually used like this:
|
||||
*
|
||||
* ```
|
||||
* lateinit var account: Account
|
||||
*
|
||||
* @Before
|
||||
* fun setUp() {
|
||||
* account = TestAccountAuthenticator.create()
|
||||
*
|
||||
* // You can now use the test account.
|
||||
* }
|
||||
*
|
||||
* @After
|
||||
* fun tearDown() {
|
||||
* TestAccountAuthenticator.remove(account)
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class TestAccountAuthenticator: Service() {
|
||||
|
||||
companion object {
|
||||
|
||||
val context by lazy { InstrumentationRegistry.getInstrumentation().context }
|
||||
|
||||
/**
|
||||
* Creates a test account, usually in the `Before` setUp of a test.
|
||||
*
|
||||
* Remove it with [remove].
|
||||
*/
|
||||
fun create(): Account {
|
||||
val accountType = context.getString(R.string.account_type_test)
|
||||
val account = Account("Test Account", accountType)
|
||||
|
||||
assertTrue(SystemAccountUtils.createAccount(context, account, AccountSettings.initialUserData(null)))
|
||||
|
||||
return account
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a test account, usually in the `@After` tearDown of a test.
|
||||
*/
|
||||
fun remove(account: Account) {
|
||||
val am = AccountManager.get(context)
|
||||
am.removeAccountExplicitly(account)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private lateinit var accountAuthenticator: AccountAuthenticator
|
||||
|
||||
|
||||
override fun onCreate() {
|
||||
accountAuthenticator = AccountAuthenticator(this)
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?) =
|
||||
accountAuthenticator.iBinder.takeIf { intent?.action == AccountManager.ACTION_AUTHENTICATOR_INTENT }
|
||||
|
||||
|
||||
private class AccountAuthenticator(
|
||||
val context: Context
|
||||
): AbstractAccountAuthenticator(context) {
|
||||
|
||||
override fun addAccount(response: AccountAuthenticatorResponse?, accountType: String?, authTokenType: String?, requiredFeatures: Array<String>?, options: Bundle?) = null
|
||||
override fun editProperties(response: AccountAuthenticatorResponse?, accountType: String?) = null
|
||||
override fun getAuthTokenLabel(p0: String?) = null
|
||||
override fun confirmCredentials(p0: AccountAuthenticatorResponse?, p1: Account?, p2: Bundle?) = null
|
||||
override fun updateCredentials(p0: AccountAuthenticatorResponse?, p1: Account?, p2: String?, p3: Bundle?) = null
|
||||
override fun getAuthToken(p0: AccountAuthenticatorResponse?, p1: Account?, p2: String?, p3: Bundle?) = null
|
||||
override fun hasFeatures(p0: AccountAuthenticatorResponse?, p1: Account?, p2: Array<out String>?) = null
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
**************************************************************************************************/
|
||||
|
||||
package at.bitfire.davdroid.sync.worker
|
||||
|
||||
import android.accounts.Account
|
||||
import android.content.Context
|
||||
import android.provider.CalendarContract
|
||||
import android.util.Log
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.work.Configuration
|
||||
import androidx.work.ListenableWorker
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkerFactory
|
||||
import androidx.work.WorkerParameters
|
||||
import androidx.work.testing.TestListenableWorkerBuilder
|
||||
import androidx.work.testing.WorkManagerTestInitHelper
|
||||
import androidx.work.workDataOf
|
||||
import at.bitfire.davdroid.sync.account.TestAccountAuthenticator
|
||||
import at.bitfire.davdroid.test.R
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class PeriodicSyncWorkerTest {
|
||||
|
||||
@Inject
|
||||
@ApplicationContext
|
||||
lateinit var context: Context
|
||||
val testContext = InstrumentationRegistry.getInstrumentation().context
|
||||
|
||||
@Inject
|
||||
lateinit var syncWorkerFactory: PeriodicSyncWorker.Factory
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
lateinit var account: Account
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
hiltRule.inject()
|
||||
|
||||
// Initialize WorkManager for instrumentation tests.
|
||||
val config = Configuration.Builder()
|
||||
.setMinimumLoggingLevel(Log.DEBUG)
|
||||
.build()
|
||||
WorkManagerTestInitHelper.initializeTestWorkManager(context, config)
|
||||
|
||||
account = TestAccountAuthenticator.create()
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
TestAccountAuthenticator.remove(account)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun doWork_cancelsItselfOnInvalidAccount() {
|
||||
val invalidAccount = Account("invalid", testContext.getString(R.string.account_type_test))
|
||||
|
||||
// Run PeriodicSyncWorker as TestWorker
|
||||
val inputData = workDataOf(
|
||||
BaseSyncWorker.INPUT_AUTHORITY to CalendarContract.AUTHORITY,
|
||||
BaseSyncWorker.INPUT_ACCOUNT_NAME to invalidAccount.name,
|
||||
BaseSyncWorker.INPUT_ACCOUNT_TYPE to invalidAccount.type
|
||||
)
|
||||
|
||||
// mock WorkManager to observe cancellation call
|
||||
val workManager = WorkManager.getInstance(context)
|
||||
mockkObject(workManager)
|
||||
|
||||
// run test worker, expect failure
|
||||
val testWorker = TestListenableWorkerBuilder<PeriodicSyncWorker>(context, inputData)
|
||||
.setWorkerFactory(object: WorkerFactory() {
|
||||
override fun createWorker(appContext: Context, workerClassName: String, workerParameters: WorkerParameters) =
|
||||
syncWorkerFactory.create(appContext, workerParameters)
|
||||
})
|
||||
.build()
|
||||
val result = runBlocking {
|
||||
testWorker.doWork()
|
||||
}
|
||||
assertTrue(result is ListenableWorker.Result.Failure)
|
||||
|
||||
// verify that worker called WorkManager.cancelWorkById(<its ID>)
|
||||
verify {
|
||||
workManager.cancelWorkById(testWorker.id)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,280 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.sync.worker
|
||||
|
||||
import android.accounts.Account
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
|
||||
import android.net.NetworkCapabilities.TRANSPORT_WIFI
|
||||
import android.net.wifi.WifiInfo
|
||||
import android.net.wifi.WifiManager
|
||||
import androidx.core.content.getSystemService
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import at.bitfire.davdroid.sync.SyncConditions
|
||||
import at.bitfire.davdroid.util.PermissionUtils
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.junit4.MockKRule
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.spyk
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class SyncConditionsTest {
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@get:Rule
|
||||
val mockkRule = MockKRule(this)
|
||||
|
||||
@MockK
|
||||
lateinit var capabilities: NetworkCapabilities
|
||||
|
||||
@Inject
|
||||
@ApplicationContext
|
||||
lateinit var context: Context
|
||||
|
||||
@Inject
|
||||
lateinit var factory: SyncConditions.Factory
|
||||
|
||||
@MockK
|
||||
lateinit var network1: Network
|
||||
|
||||
@MockK
|
||||
lateinit var network2: Network
|
||||
|
||||
|
||||
private lateinit var accountSettings: AccountSettings
|
||||
|
||||
private lateinit var conditions: SyncConditions
|
||||
|
||||
private lateinit var connectivityManager: ConnectivityManager
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
hiltRule.inject()
|
||||
|
||||
// prepare accountSettings with some necessary data
|
||||
accountSettings = mockk<AccountSettings> {
|
||||
every { account } returns Account("test", "test")
|
||||
every { getIgnoreVpns() } returns false // default value
|
||||
}
|
||||
|
||||
conditions = factory.create(accountSettings)
|
||||
|
||||
connectivityManager = context.getSystemService<ConnectivityManager>()!!.also { cm ->
|
||||
mockkObject(cm)
|
||||
every { cm.allNetworks } returns arrayOf(network1, network2)
|
||||
every { cm.getNetworkInfo(network1) } returns mockk()
|
||||
every { cm.getNetworkInfo(network2) } returns mockk()
|
||||
every { cm.getNetworkCapabilities(network1) } returns capabilities
|
||||
every { cm.getNetworkCapabilities(network2) } returns capabilities
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testCorrectWifiSsid_CorrectWiFiSsid() {
|
||||
every { accountSettings.getSyncWifiOnlySSIDs() } returns listOf("SampleWiFi1","ConnectedWiFi")
|
||||
|
||||
mockkObject(PermissionUtils)
|
||||
every { PermissionUtils.canAccessWifiSsid(any()) } returns true
|
||||
|
||||
val wifiManager = context.getSystemService<WifiManager>()!!
|
||||
mockkObject(wifiManager)
|
||||
every { wifiManager.connectionInfo } returns spyk<WifiInfo>().apply {
|
||||
every { ssid } returns "ConnectedWiFi"
|
||||
}
|
||||
|
||||
assertTrue(conditions.correctWifiSsid())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCorrectWifiSsid_WrongWiFiSsid() {
|
||||
every { accountSettings.getSyncWifiOnlySSIDs() } returns listOf("SampleWiFi1","SampleWiFi2")
|
||||
|
||||
mockkObject(PermissionUtils)
|
||||
every { PermissionUtils.canAccessWifiSsid(any()) } returns true
|
||||
|
||||
val wifiManager = context.getSystemService<WifiManager>()!!
|
||||
mockkObject(wifiManager)
|
||||
every { wifiManager.connectionInfo } returns spyk<WifiInfo>().apply {
|
||||
every { ssid } returns "ConnectedWiFi"
|
||||
}
|
||||
|
||||
assertFalse(conditions.correctWifiSsid())
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testInternetAvailable_capabilitiesNull() {
|
||||
every { connectivityManager.getNetworkCapabilities(network1) } returns null
|
||||
every { connectivityManager.getNetworkCapabilities(network2) } returns null
|
||||
assertFalse(conditions.internetAvailable())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInternetAvailable_Internet() {
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_INTERNET) } returns true
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_VALIDATED) } returns false
|
||||
assertFalse(conditions.internetAvailable())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInternetAvailable_Validated() {
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_INTERNET) } returns false
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_VALIDATED) } returns true
|
||||
assertFalse(conditions.internetAvailable())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInternetAvailable_InternetValidated() {
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_INTERNET) } returns true
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_VALIDATED) } returns true
|
||||
assertTrue(conditions.internetAvailable())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInternetAvailable_ignoreVpns() {
|
||||
every { accountSettings.getIgnoreVpns() } returns true
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_INTERNET) } returns true
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_VALIDATED) } returns true
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_NOT_VPN) } returns false
|
||||
assertFalse(conditions.internetAvailable())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInternetAvailable_ignoreVpns_NotVpn() {
|
||||
every { accountSettings.getIgnoreVpns() } returns true
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_INTERNET) } returns true
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_VALIDATED) } returns true
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_NOT_VPN) } returns true
|
||||
assertTrue(conditions.internetAvailable())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInternetAvailable_twoConnectionsFirstOneWithoutInternet() {
|
||||
// The real case that failed in davx5-ose#395 is that the connection list contains (in this order)
|
||||
// 1. a mobile network without INTERNET, but with VALIDATED
|
||||
// 2. a WiFi network with INTERNET and VALIDATED
|
||||
|
||||
// The "return false" of hasINTERNET will trigger at the first connection, the
|
||||
// "andThen true" will trigger for the second connection
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_INTERNET) } returns false andThen true
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_VALIDATED) } returns true
|
||||
|
||||
// There is an internet connection if any(!) connection has both INTERNET and VALIDATED.
|
||||
assertTrue(conditions.internetAvailable())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInternetAvailable_twoConnectionsFirstOneWithoutValidated() {
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_INTERNET) } returns true
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_VALIDATED) } returns false andThen true
|
||||
assertTrue(conditions.internetAvailable())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInternetAvailable_twoConnectionsFirstOneWithoutNotVpn() {
|
||||
every { accountSettings.getIgnoreVpns() } returns true
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_INTERNET) } returns true
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_VALIDATED) } returns true
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_NOT_VPN) } returns false andThen true
|
||||
assertTrue(conditions.internetAvailable())
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testWifiAvailable_capabilitiesNull() {
|
||||
every { connectivityManager.getNetworkCapabilities(network1) } returns null
|
||||
every { connectivityManager.getNetworkCapabilities(network2) } returns null
|
||||
assertFalse(conditions.wifiAvailable())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWifiAvailable() {
|
||||
every { capabilities.hasTransport(TRANSPORT_WIFI) } returns false
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_VALIDATED) } returns false
|
||||
assertFalse(conditions.wifiAvailable())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWifiAvailable_wifi() {
|
||||
every { capabilities.hasTransport(TRANSPORT_WIFI) } returns true
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_VALIDATED) } returns false
|
||||
assertFalse(conditions.wifiAvailable())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWifiAvailable_validated() {
|
||||
every { capabilities.hasTransport(TRANSPORT_WIFI) } returns false
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_VALIDATED) } returns true
|
||||
assertFalse(conditions.wifiAvailable())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWifiAvailable_wifiValidated() {
|
||||
every { capabilities.hasTransport(TRANSPORT_WIFI) } returns true
|
||||
every { capabilities.hasCapability(NET_CAPABILITY_VALIDATED) } returns true
|
||||
assertTrue(conditions.wifiAvailable())
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testWifiConditionsMet_withoutWifi() {
|
||||
// "Sync only over Wi-Fi" is disabled
|
||||
every { accountSettings.getSyncWifiOnly() } returns false
|
||||
|
||||
assertTrue(factory.create(accountSettings).wifiConditionsMet())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWifiConditionsMet_anyWifi_wifiEnabled() {
|
||||
// "Sync only over Wi-Fi" is enabled
|
||||
every { accountSettings.getSyncWifiOnly() } returns true
|
||||
|
||||
// Wi-Fi is available
|
||||
mockkObject(conditions) {
|
||||
// Wi-Fi is available
|
||||
every { conditions.wifiAvailable() } returns true
|
||||
|
||||
// Wi-Fi SSID is correct
|
||||
every { conditions.correctWifiSsid() } returns true
|
||||
|
||||
assertTrue(conditions.wifiConditionsMet())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWifiConditionsMet_anyWifi_wifiDisabled() {
|
||||
// "Sync only over Wi-Fi" is enabled
|
||||
every { accountSettings.getSyncWifiOnly() } returns true
|
||||
|
||||
mockkObject(conditions) {
|
||||
// Wi-Fi is not available
|
||||
every { conditions.wifiAvailable() } returns false
|
||||
|
||||
// Wi-Fi SSID is correct
|
||||
every { conditions.correctWifiSsid() } returns true
|
||||
|
||||
assertFalse(conditions.wifiConditionsMet())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.sync.worker
|
||||
|
||||
import android.accounts.Account
|
||||
import android.content.Context
|
||||
import android.provider.CalendarContract
|
||||
import android.util.Log
|
||||
import androidx.hilt.work.HiltWorkerFactory
|
||||
import androidx.work.Configuration
|
||||
import androidx.work.testing.WorkManagerTestInitHelper
|
||||
import at.bitfire.davdroid.TestUtils
|
||||
import at.bitfire.davdroid.TestUtils.workScheduledOrRunning
|
||||
import at.bitfire.davdroid.sync.account.TestAccountAuthenticator
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class SyncWorkerManagerTest {
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
@ApplicationContext
|
||||
lateinit var context: Context
|
||||
|
||||
@Inject
|
||||
lateinit var syncWorkerManager: SyncWorkerManager
|
||||
|
||||
@Inject
|
||||
lateinit var workerFactory: HiltWorkerFactory
|
||||
|
||||
lateinit var account: Account
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
hiltRule.inject()
|
||||
|
||||
// Initialize WorkManager for instrumentation tests.
|
||||
val config = Configuration.Builder()
|
||||
.setMinimumLoggingLevel(Log.DEBUG)
|
||||
.setWorkerFactory(workerFactory)
|
||||
.build()
|
||||
WorkManagerTestInitHelper.initializeTestWorkManager(context, config)
|
||||
|
||||
account = TestAccountAuthenticator.create()
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
TestAccountAuthenticator.remove(account)
|
||||
}
|
||||
|
||||
|
||||
// one-time sync workers
|
||||
|
||||
@Test
|
||||
fun testEnqueueOneTime() {
|
||||
val workerName = OneTimeSyncWorker.workerName(account, CalendarContract.AUTHORITY)
|
||||
assertFalse(TestUtils.workScheduledOrRunningOrSuccessful(context, workerName))
|
||||
|
||||
val returnedName = syncWorkerManager.enqueueOneTime(account, CalendarContract.AUTHORITY)
|
||||
assertEquals(workerName, returnedName)
|
||||
assertTrue(TestUtils.workScheduledOrRunningOrSuccessful(context, workerName))
|
||||
}
|
||||
|
||||
|
||||
// periodic sync workers
|
||||
|
||||
@Test
|
||||
fun enablePeriodic() {
|
||||
syncWorkerManager.enablePeriodic(account, CalendarContract.AUTHORITY, 60, false).result.get()
|
||||
|
||||
val workerName = PeriodicSyncWorker.workerName(account, CalendarContract.AUTHORITY)
|
||||
assertTrue(workScheduledOrRunning(context, workerName))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun disablePeriodic() {
|
||||
syncWorkerManager.enablePeriodic(account, CalendarContract.AUTHORITY, 60, false).result.get()
|
||||
syncWorkerManager.disablePeriodic(account, CalendarContract.AUTHORITY).result.get()
|
||||
|
||||
val workerName = PeriodicSyncWorker.workerName(account, CalendarContract.AUTHORITY)
|
||||
assertFalse(workScheduledOrRunning(context, workerName))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.ui
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class DebugInfoActivityTest {
|
||||
|
||||
@Test
|
||||
fun testIntentBuilder_LargeLocalResource() {
|
||||
val a = 'A'.code.toByte()
|
||||
val intent = DebugInfoActivity.IntentBuilder(InstrumentationRegistry.getInstrumentation().context)
|
||||
.withLocalResource(String(ByteArray(1024*1024) { a }))
|
||||
.build()
|
||||
val expected = StringBuilder(DebugInfoActivity.IntentBuilder.MAX_ELEMENT_SIZE)
|
||||
expected.append(String(ByteArray(DebugInfoActivity.IntentBuilder.MAX_ELEMENT_SIZE - 3) { a }))
|
||||
expected.append("...")
|
||||
assertEquals(expected.toString(), intent.getStringExtra("localResource"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIntentBuilder_LargeLogs() {
|
||||
val a = 'A'.code.toByte()
|
||||
val intent = DebugInfoActivity.IntentBuilder(InstrumentationRegistry.getInstrumentation().context)
|
||||
.withLogs(String(ByteArray(1024*1024) { a }))
|
||||
.build()
|
||||
val expected = StringBuilder(DebugInfoActivity.IntentBuilder.MAX_ELEMENT_SIZE)
|
||||
expected.append(String(ByteArray(DebugInfoActivity.IntentBuilder.MAX_ELEMENT_SIZE - 3) { a }))
|
||||
expected.append("...")
|
||||
assertEquals(expected.toString(), intent.getStringExtra("logs"))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.webdav
|
||||
|
||||
import at.bitfire.davdroid.db.Credentials
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class CredentialsStoreTest {
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
lateinit var store: CredentialsStore
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
hiltRule.inject()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetGetDelete() {
|
||||
store.setCredentials(0, Credentials(username = "myname", password = "12345"))
|
||||
assertEquals(Credentials(username = "myname", password = "12345"), store.getCredentials(0))
|
||||
|
||||
store.setCredentials(0, null)
|
||||
assertNull(store.getCredentials(0))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,254 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.webdav
|
||||
|
||||
import android.content.Context
|
||||
import android.security.NetworkSecurityPolicy
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.db.AppDatabase
|
||||
import at.bitfire.davdroid.db.WebDavDocument
|
||||
import at.bitfire.davdroid.db.WebDavMount
|
||||
import at.bitfire.davdroid.network.HttpClient
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import okhttp3.CookieJar
|
||||
import okhttp3.mockwebserver.Dispatcher
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import okhttp3.mockwebserver.RecordedRequest
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class DavDocumentsProviderTest {
|
||||
|
||||
companion object {
|
||||
private const val PATH_WEBDAV_ROOT = "/webdav"
|
||||
}
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
@ApplicationContext
|
||||
lateinit var context: Context
|
||||
|
||||
@Inject
|
||||
lateinit var db: AppDatabase
|
||||
|
||||
@Inject
|
||||
lateinit var logger: java.util.logging.Logger
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
hiltRule.inject()
|
||||
}
|
||||
|
||||
|
||||
private var mockServer = MockWebServer()
|
||||
|
||||
private lateinit var client: HttpClient
|
||||
|
||||
|
||||
@Before
|
||||
fun mockServerSetup() {
|
||||
// Start mock web server
|
||||
mockServer.dispatcher = TestDispatcher(logger)
|
||||
mockServer.start()
|
||||
|
||||
client = HttpClient.Builder(context).build()
|
||||
|
||||
// mock server delivers HTTP without encryption
|
||||
assertTrue(NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted)
|
||||
}
|
||||
|
||||
@After
|
||||
fun cleanUp() {
|
||||
mockServer.shutdown()
|
||||
db.close()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testDoQueryChildren_insert() {
|
||||
// Create parent and root in database
|
||||
val id = db.webDavMountDao().insert(WebDavMount(0, "Cat food storage", mockServer.url(PATH_WEBDAV_ROOT)))
|
||||
val webDavMount = db.webDavMountDao().getById(id)
|
||||
val parent = db.webDavDocumentDao().getOrCreateRoot(webDavMount)
|
||||
val cookieStore = mutableMapOf<Long, CookieJar>()
|
||||
|
||||
// Query
|
||||
DavDocumentsProvider.DavDocumentsActor(context, db, logger, cookieStore, CredentialsStore(context), context.getString(R.string.webdav_authority))
|
||||
.queryChildren(parent)
|
||||
|
||||
// Assert new children were inserted into db
|
||||
assertEquals(3, db.webDavDocumentDao().getChildren(parent.id).size)
|
||||
assertEquals("Secret_Document.pages", db.webDavDocumentDao().getChildren(parent.id)[0].displayName)
|
||||
assertEquals("MeowMeow_Cats.docx", db.webDavDocumentDao().getChildren(parent.id)[1].displayName)
|
||||
assertEquals("Library", db.webDavDocumentDao().getChildren(parent.id)[2].displayName)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDoQueryChildren_update() {
|
||||
// Create parent and root in database
|
||||
val mountId = db.webDavMountDao().insert(WebDavMount(0, "Cat food storage", mockServer.url(PATH_WEBDAV_ROOT)))
|
||||
val webDavMount = db.webDavMountDao().getById(mountId)
|
||||
val parent = db.webDavDocumentDao().getOrCreateRoot(webDavMount)
|
||||
val cookieStore = mutableMapOf<Long, CookieJar>()
|
||||
assertEquals("Cat food storage", db.webDavDocumentDao().get(parent.id)!!.displayName)
|
||||
|
||||
// Create a folder
|
||||
val folderId = db.webDavDocumentDao().insert(
|
||||
WebDavDocument(
|
||||
0,
|
||||
mountId,
|
||||
parent.id,
|
||||
"My_Books",
|
||||
true,
|
||||
"My Books",
|
||||
)
|
||||
)
|
||||
assertEquals("My_Books", db.webDavDocumentDao().get(folderId)!!.name)
|
||||
assertEquals("My Books", db.webDavDocumentDao().get(folderId)!!.displayName)
|
||||
|
||||
// Query - should update the parent displayname and folder name
|
||||
DavDocumentsProvider.DavDocumentsActor(context, db, logger, cookieStore, CredentialsStore(context), context.getString(R.string.webdav_authority))
|
||||
.queryChildren(parent)
|
||||
|
||||
// Assert parent and children were updated in database
|
||||
assertEquals("Cats WebDAV", db.webDavDocumentDao().get(parent.id)!!.displayName)
|
||||
assertEquals("Library", db.webDavDocumentDao().getChildren(parent.id)[2].name)
|
||||
assertEquals("Library", db.webDavDocumentDao().getChildren(parent.id)[2].displayName)
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDoQueryChildren_delete() {
|
||||
// Create parent and root in database
|
||||
val mountId = db.webDavMountDao().insert(WebDavMount(0, "Cat food storage", mockServer.url(PATH_WEBDAV_ROOT)))
|
||||
val webDavMount = db.webDavMountDao().getById(mountId)
|
||||
val parent = db.webDavDocumentDao().getOrCreateRoot(webDavMount)
|
||||
val cookieStore = mutableMapOf<Long, CookieJar>()
|
||||
|
||||
// Create a folder
|
||||
val folderId = db.webDavDocumentDao().insert(
|
||||
WebDavDocument(0, mountId, parent.id, "deleteme", true, "Should be deleted")
|
||||
)
|
||||
assertEquals("deleteme", db.webDavDocumentDao().get(folderId)!!.name)
|
||||
|
||||
// Query - discovers serverside deletion
|
||||
DavDocumentsProvider.DavDocumentsActor(context, db, logger, cookieStore, CredentialsStore(context), context.getString(R.string.webdav_authority))
|
||||
.queryChildren(parent)
|
||||
|
||||
// Assert folder got deleted
|
||||
assertEquals(null, db.webDavDocumentDao().get(folderId))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDoQueryChildren_updateTwoParentsSimultaneous() {
|
||||
// Create root in database
|
||||
val mountId = db.webDavMountDao().insert(WebDavMount(0, "Cat food storage", mockServer.url(PATH_WEBDAV_ROOT)))
|
||||
val webDavMount = db.webDavMountDao().getById(mountId)
|
||||
val root = db.webDavDocumentDao().getOrCreateRoot(webDavMount)
|
||||
val cookieStore = mutableMapOf<Long, CookieJar>()
|
||||
|
||||
// Create two parents
|
||||
val parent1Id = db.webDavDocumentDao().insert(WebDavDocument(0, mountId, root.id, "parent1", true))
|
||||
val parent2Id = db.webDavDocumentDao().insert(WebDavDocument(0, mountId, root.id, "parent2", true))
|
||||
val parent1 = db.webDavDocumentDao().get(parent1Id)!!
|
||||
val parent2 = db.webDavDocumentDao().get(parent2Id)!!
|
||||
assertEquals("parent1", parent1.name)
|
||||
assertEquals("parent2", parent2.name)
|
||||
|
||||
// Query - find children of two nodes simultaneously
|
||||
DavDocumentsProvider.DavDocumentsActor(context, db, logger, cookieStore, CredentialsStore(context), context.getString(R.string.webdav_authority))
|
||||
.queryChildren(parent1)
|
||||
DavDocumentsProvider.DavDocumentsActor(context, db, logger, cookieStore, CredentialsStore(context), context.getString(R.string.webdav_authority))
|
||||
.queryChildren(parent2)
|
||||
|
||||
// Assert the two folders names have changed
|
||||
assertEquals("childOne.txt", db.webDavDocumentDao().getChildren(parent1Id)[0].name)
|
||||
assertEquals("childTwo.txt", db.webDavDocumentDao().getChildren(parent2Id)[0].name)
|
||||
}
|
||||
|
||||
|
||||
// mock server
|
||||
|
||||
class TestDispatcher(
|
||||
private val logger: java.util.logging.Logger
|
||||
): Dispatcher() {
|
||||
|
||||
data class Resource(
|
||||
val name: String,
|
||||
val props: String
|
||||
)
|
||||
|
||||
override fun dispatch(request: RecordedRequest): MockResponse {
|
||||
val requestPath = request.path!!.trimEnd('/')
|
||||
|
||||
if (request.method.equals("PROPFIND", true)) {
|
||||
|
||||
val propsMap = mutableMapOf(
|
||||
PATH_WEBDAV_ROOT to arrayOf(
|
||||
Resource("",
|
||||
"<resourcetype><collection/></resourcetype>" +
|
||||
"<displayname>Cats WebDAV</displayname>"
|
||||
),
|
||||
Resource("Secret_Document.pages",
|
||||
"<displayname>Secret_Document.pages</displayname>",
|
||||
),
|
||||
Resource("MeowMeow_Cats.docx",
|
||||
"<displayname>MeowMeow_Cats.docx</displayname>"
|
||||
),
|
||||
Resource("Library",
|
||||
"<resourcetype><collection/></resourcetype>" +
|
||||
"<displayname>Library</displayname>"
|
||||
)
|
||||
),
|
||||
|
||||
"$PATH_WEBDAV_ROOT/parent1" to arrayOf(
|
||||
Resource("childOne.txt",
|
||||
"<displayname>childOne.txt</displayname>"
|
||||
),
|
||||
),
|
||||
"$PATH_WEBDAV_ROOT/parent2" to arrayOf(
|
||||
Resource("childTwo.txt",
|
||||
"<displayname>childTwo.txt</displayname>"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val responses = propsMap[requestPath]?.joinToString { resource ->
|
||||
"<response><href>$requestPath/${resource.name}</href><propstat><prop>" +
|
||||
resource.props +
|
||||
"</prop></propstat></response>"
|
||||
}
|
||||
|
||||
val multistatus =
|
||||
"<multistatus xmlns='DAV:' " +
|
||||
"xmlns:CARD='urn:ietf:params:xml:ns:carddav' " +
|
||||
"xmlns:CAL='urn:ietf:params:xml:ns:caldav'>" +
|
||||
responses +
|
||||
"</multistatus>"
|
||||
|
||||
logger.info("Query path: $requestPath")
|
||||
logger.info("Response: $multistatus")
|
||||
return MockResponse()
|
||||
.setResponseCode(207)
|
||||
.setBody(multistatus)
|
||||
}
|
||||
|
||||
return MockResponse().setResponseCode(404)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.webdav
|
||||
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class WebDavMountRepositoryTest {
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Inject
|
||||
lateinit var repository: WebDavMountRepository
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
hiltRule.inject()
|
||||
}
|
||||
|
||||
val web = MockWebServer()
|
||||
val url = web.url("/")
|
||||
|
||||
@Test
|
||||
fun testHasWebDav_NoDavHeader() = runBlocking {
|
||||
web.enqueue(MockResponse().setResponseCode(200))
|
||||
assertFalse(repository.hasWebDav(url, null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHasWebDav_DavClass1() = runBlocking {
|
||||
web.enqueue(MockResponse()
|
||||
.setResponseCode(200)
|
||||
.addHeader("DAV: 1"))
|
||||
assertTrue(repository.hasWebDav(url, null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHasWebDav_DavClass2() = runBlocking {
|
||||
web.enqueue(MockResponse()
|
||||
.setResponseCode(200)
|
||||
.addHeader("DAV: 1, 2"))
|
||||
assertTrue(repository.hasWebDav(url, null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHasWebDav_DavClass3() = runBlocking {
|
||||
web.enqueue(MockResponse()
|
||||
.setResponseCode(200)
|
||||
.addHeader("DAV: 1, 3"))
|
||||
assertTrue(repository.hasWebDav(url, null))
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 9.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 5.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB |
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Ricki Hirner (bitfire web engineering).
|
||||
~ Copyright (c) 2013 – 2015 Ricki Hirner (bitfire web engineering).
|
||||
~ All rights reserved. This program and the accompanying materials
|
||||
~ are made available under the terms of the GNU Public License v3.0
|
||||
~ which accompanies this distribution, and is available at
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
<resources>
|
||||
|
||||
<string name="app_name">Davx5Test</string>
|
||||
<string name="account_type_test">at.bitfire.davdroid.test</string>
|
||||
<string name="app_name">DavdroidTest</string>
|
||||
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="@string/account_type_test"
|
||||
android:icon="@android:drawable/star_on"
|
||||
android:smallIcon="@android:drawable/star_on"
|
||||
android:label="Test Account" />
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright © Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import at.bitfire.davdroid.App
|
||||
import at.bitfire.davdroid.BuildConfig
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.settings.ISettings
|
||||
|
||||
class DefaultAccountsDrawerHandler: IAccountsDrawerHandler {
|
||||
|
||||
override fun onSettingsChanged(settings: ISettings?, menu: Menu) {
|
||||
if (BuildConfig.VERSION_NAME.contains("-beta") || BuildConfig.VERSION_NAME.contains("-rc"))
|
||||
menu.findItem(R.id.nav_beta_feedback).isVisible = true
|
||||
}
|
||||
|
||||
override fun onNavigationItemSelected(activity: Activity, item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.nav_about ->
|
||||
activity.startActivity(Intent(activity, AboutActivity::class.java))
|
||||
R.id.nav_app_settings ->
|
||||
activity.startActivity(Intent(activity, AppSettingsActivity::class.java))
|
||||
R.id.nav_beta_feedback ->
|
||||
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(activity.getString(R.string.beta_feedback_url))))
|
||||
R.id.nav_twitter ->
|
||||
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://twitter.com/davdroidapp")))
|
||||
R.id.nav_website ->
|
||||
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(activity.getString(R.string.homepage_url))))
|
||||
R.id.nav_faq ->
|
||||
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(activity.getString(R.string.navigation_drawer_faq_url))))
|
||||
R.id.nav_forums ->
|
||||
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(activity.getString(R.string.homepage_url))
|
||||
.buildUpon().appendEncodedPath("forums/").build()))
|
||||
R.id.nav_donate ->
|
||||
if (BuildConfig.FLAVOR != App.FLAVOR_GOOGLE_PLAY)
|
||||
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(activity.getString(R.string.homepage_url))
|
||||
.buildUpon().appendEncodedPath("donate/").build()))
|
||||
else ->
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright © Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.ui.setup
|
||||
|
||||
import android.app.Fragment
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CompoundButton
|
||||
import at.bitfire.dav4android.Constants
|
||||
import at.bitfire.davdroid.R
|
||||
import kotlinx.android.synthetic.standard.login_credentials_fragment.view.*
|
||||
import java.net.IDN
|
||||
import java.net.URI
|
||||
import java.net.URISyntaxException
|
||||
import java.util.logging.Level
|
||||
|
||||
class DefaultLoginCredentialsFragment: Fragment(), CompoundButton.OnCheckedChangeListener {
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
val v = inflater.inflate(R.layout.login_credentials_fragment, container, false)
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
// first call
|
||||
activity?.intent?.let {
|
||||
// we've got initial login data
|
||||
val url = it.getStringExtra(LoginActivity.EXTRA_URL)
|
||||
val username = it.getStringExtra(LoginActivity.EXTRA_USERNAME)
|
||||
val password = it.getStringExtra(LoginActivity.EXTRA_PASSWORD)
|
||||
|
||||
if (url != null) {
|
||||
v.login_type_url.isChecked = true
|
||||
v.base_url.setText(url)
|
||||
v.user_name.setText(username)
|
||||
v.url_password.setText(password)
|
||||
} else {
|
||||
v.login_type_email.isChecked = true
|
||||
v.email_address.setText(username)
|
||||
v.email_password.setText(password)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v.login.setOnClickListener({ _ ->
|
||||
validateLoginData()?.let { credentials ->
|
||||
DetectConfigurationFragment.newInstance(credentials).show(fragmentManager, null)
|
||||
}
|
||||
})
|
||||
|
||||
// initialize to Login by email
|
||||
onCheckedChanged(v)
|
||||
|
||||
v.login_type_email.setOnCheckedChangeListener(this)
|
||||
v.login_type_url.setOnCheckedChangeListener(this)
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
|
||||
onCheckedChanged(view)
|
||||
}
|
||||
|
||||
private fun onCheckedChanged(v: View) {
|
||||
val loginByEmail = !v.login_type_url.isChecked
|
||||
v.login_type_email_details.visibility = if (loginByEmail) View.VISIBLE else View.GONE
|
||||
v.login_type_url_details.visibility = if (loginByEmail) View.GONE else View.VISIBLE
|
||||
(if (loginByEmail) v.email_address else v.base_url).requestFocus()
|
||||
}
|
||||
|
||||
private fun validateLoginData(): LoginCredentials? {
|
||||
if (view.login_type_email.isChecked) {
|
||||
var uri: URI? = null
|
||||
var valid = true
|
||||
|
||||
val email = view.email_address.text.toString()
|
||||
if (!email.matches(Regex(".+@.+"))) {
|
||||
view.email_address.error = getString(R.string.login_email_address_error)
|
||||
valid = false
|
||||
} else
|
||||
try {
|
||||
uri = URI("mailto", email, null)
|
||||
} catch(e: URISyntaxException) {
|
||||
view.email_address.error = e.localizedMessage
|
||||
valid = false
|
||||
}
|
||||
|
||||
val password = view.email_password.getText().toString()
|
||||
if (password.isEmpty()) {
|
||||
view.email_password.setError(getString(R.string.login_password_required))
|
||||
valid = false
|
||||
}
|
||||
|
||||
return if (valid && uri != null)
|
||||
LoginCredentials(uri, email, password)
|
||||
else
|
||||
null
|
||||
|
||||
} else if (view.login_type_url.isChecked) {
|
||||
var uri: URI? = null
|
||||
var valid = true
|
||||
|
||||
val baseUrl = Uri.parse(view.base_url.text.toString())
|
||||
val scheme = baseUrl.scheme
|
||||
if (scheme.equals("http", true) || scheme.equals("https", true)) {
|
||||
var host = baseUrl.host
|
||||
if (host.isNullOrBlank()) {
|
||||
view.base_url.error = getString(R.string.login_url_host_name_required)
|
||||
valid = false
|
||||
} else
|
||||
try {
|
||||
host = IDN.toASCII(host)
|
||||
} catch(e: IllegalArgumentException) {
|
||||
Constants.log.log(Level.WARNING, "Host name not conforming to RFC 3490", e)
|
||||
}
|
||||
|
||||
val path = baseUrl.encodedPath
|
||||
val port = baseUrl.port
|
||||
try {
|
||||
uri = URI(baseUrl.scheme, null, host, port, path, null, null)
|
||||
} catch(e: URISyntaxException) {
|
||||
view.base_url.error = e.localizedMessage
|
||||
valid = false
|
||||
}
|
||||
} else {
|
||||
view.base_url.error = getString(R.string.login_url_must_be_http_or_https)
|
||||
valid = false
|
||||
}
|
||||
|
||||
val userName = view.user_name.text.toString()
|
||||
if (userName.isBlank()) {
|
||||
view.user_name.error = getString(R.string.login_user_name_required)
|
||||
valid = false
|
||||
}
|
||||
|
||||
val password = view.url_password.getText().toString()
|
||||
if (password.isEmpty()) {
|
||||
view.url_password.setError(getString(R.string.login_password_required))
|
||||
valid = false
|
||||
}
|
||||
|
||||
return if (valid && uri != null)
|
||||
LoginCredentials(uri, userName, password)
|
||||
else
|
||||
null
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
class Factory: ILoginCredentialsFragment {
|
||||
|
||||
override fun getFragment() = DefaultLoginCredentialsFragment()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
120
app/src/davdroid/res/layout/login_credentials_fragment.xml
Normal file
120
app/src/davdroid/res/layout/login_credentials_fragment.xml
Normal file
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
||||
~ All rights reserved. This program and the accompanying materials
|
||||
~ are made available under the terms of the GNU Public License v3.0
|
||||
~ which accompanies this distribution, and is available at
|
||||
~ http://www.gnu.org/licenses/gpl.html
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<!-- We don't want the keyboard up when the user arrives in this initial screen -->
|
||||
<View android:layout_height="0dp"
|
||||
android:layout_width="0dp"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:contentDescription="@null"
|
||||
android:importantForAccessibility="no" tools:ignore="UnusedAttribute">
|
||||
<requestFocus/>
|
||||
</View>
|
||||
|
||||
<ScrollView android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_margin="@dimen/activity_margin">
|
||||
|
||||
<RadioGroup
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/login_type_email"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/login_type_email"
|
||||
android:paddingLeft="14dp" tools:ignore="RtlSymmetry"
|
||||
style="@style/login_type_headline"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/login_type_email_details"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/email_address"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/login_email_address"
|
||||
android:inputType="textEmailAddress"/>
|
||||
<at.bitfire.davdroid.ui.widget.EditPassword
|
||||
android:id="@+id/email_password"
|
||||
android:hint="@string/login_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/login_type_url"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/login_type_url"
|
||||
android:layout_marginTop="16dp"
|
||||
android:paddingLeft="14dp" tools:ignore="RtlSymmetry"
|
||||
style="@style/login_type_headline"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/login_type_url_details"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/base_url"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/login_base_url"
|
||||
android:inputType="textUri"/>
|
||||
<EditText
|
||||
android:id="@+id/user_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/login_user_name"
|
||||
android:inputType="textEmailAddress"/>
|
||||
<at.bitfire.davdroid.ui.widget.EditPassword
|
||||
android:id="@+id/url_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/login_password"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RadioGroup>
|
||||
</ScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/stepper_nav_bar">
|
||||
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
style="@style/stepper_nav_button"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/login"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/login_login"
|
||||
style="@style/stepper_nav_button"/>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
22
app/src/davdroid/res/values-ca/strings.xml
Normal file
22
app/src/davdroid/res/values-ca/strings.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!--common strings-->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="help">Ajuda</string>
|
||||
<!--startup dialogs-->
|
||||
<!--AboutActivity-->
|
||||
<!--global settings-->
|
||||
<!--AccountsActivity-->
|
||||
<!--DavService-->
|
||||
<!--AppSettingsActivity-->
|
||||
<!--AccountActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_type_email">Entra amb una adreça de correu electrònic</string>
|
||||
<string name="login_type_url">Entra amb una URL i un nom d\'usuari</string>
|
||||
<!--AccountSettingsActivity-->
|
||||
<!--collection management-->
|
||||
<!--ExceptionInfoFragment-->
|
||||
<!--sync adapters and DebugInfoActivity-->
|
||||
<!--cert4android-->
|
||||
</resources>
|
||||
216
app/src/davdroid/res/values-cs/strings.xml
Normal file
216
app/src/davdroid/res/values-cs/strings.xml
Normal file
@@ -0,0 +1,216 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!--common strings-->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="help">Pomoc</string>
|
||||
<string name="manage_accounts">Spravovat účty</string>
|
||||
<string name="please_wait">Chvíli strpení ...</string>
|
||||
<string name="send">Odeslat</string>
|
||||
<!--startup dialogs-->
|
||||
<string name="startup_battery_optimization">Optimalizace využití baterie</string>
|
||||
<string name="startup_battery_optimization_message">Android může po několika dnech vypnout/prodloužit interval synchronizování DAVdroid. Chcete-li tomuto zabránit, vypněte optimalizaci baterie.</string>
|
||||
<string name="startup_battery_optimization_disable">Vypnout pro DAVdroid</string>
|
||||
<string name="startup_dont_show_again">Již nezobrazovat</string>
|
||||
<string name="startup_donate">Open Source informace</string>
|
||||
<string name="startup_donate_message">Jsme velice rádi že používáte DAVdroid, software s otevřeným zdrojovým kódem (GPLv3). Vývoj této aplikace je náročný a trval již několik tisíc hodin, velice nás potěší přispějete-li na jeho vývoj.</string>
|
||||
<string name="startup_donate_now">Zobrazit stránku pro obdarování</string>
|
||||
<string name="startup_donate_later">Možná později</string>
|
||||
<string name="startup_google_play_accounts_removed">Informace o chybě DRM Obchodu Play</string>
|
||||
<string name="startup_google_play_accounts_removed_message">Za určitých podmínek může dojít po restartu nebo aktualizaci aplikace DAVdroid k vymazání účtů kvůli chybě DRM Obchodu Play. Pokud jste postiženi touto chybou (ale pouze v tomto případě), nainstalujte prosím z Obchodu Play aplikaci \"DAVdroid JB Workaround\".</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Více informací</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks není nainstalován</string>
|
||||
<string name="startup_opentasks_not_installed_message">Aplikace OpenTasks není dostupná, proto nebude DAVdroid moci synchronizovat seznam úkolů.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Po instalaci OpenTasks musíte PŘEINSTALOVAT DAVdroid a přidat znovu své účty (Android chyba).</string>
|
||||
<string name="startup_opentasks_not_installed_install">Nainstalovat OpenTasks</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Licenční podmínky</string>
|
||||
<string name="about_license_info_no_warranty">Tento program je distribuován BEZ JAKÉKOLIV ZÁRUKY. Je to volně dostupný software a lze jej za určitých podmínek dále distribuovat.</string>
|
||||
<!--global settings-->
|
||||
<string name="logging_davdroid_file_logging">DAVdroid logování do souboru</string>
|
||||
<string name="logging_to_external_storage">Logování do externího úložiště: %s</string>
|
||||
<string name="logging_couldnt_create_file">Nelze vytvořit externí soubor logu: %s</string>
|
||||
<string name="logging_no_external_storage">Externí úložiště nenalezeno</string>
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_open">Otevřít panel navigace</string>
|
||||
<string name="navigation_drawer_close">Zavřít panel navigace</string>
|
||||
<string name="navigation_drawer_subtitle">CalDAV/CardDAV adapter synchronizace</string>
|
||||
<string name="navigation_drawer_about">O aplikaci / Licence</string>
|
||||
<string name="navigation_drawer_settings">Nastavení</string>
|
||||
<string name="navigation_drawer_news_updates">Novinky & aktualizace</string>
|
||||
<string name="navigation_drawer_external_links">Externí odkazy</string>
|
||||
<string name="navigation_drawer_website">Webová stránka</string>
|
||||
<string name="navigation_drawer_faq">FAQ</string>
|
||||
<string name="navigation_drawer_donate">Obdarovat</string>
|
||||
<string name="account_list_empty">Vítejte v aplikaci DAVdroid!\n\nNyní můžete přidat CalDAV/CardDAV účet.</string>
|
||||
<!--DavService-->
|
||||
<string name="dav_service_refresh_failed">Vyhledání služby selhalo</string>
|
||||
<string name="dav_service_refresh_couldnt_refresh">Nelze obnovit seznam sbírky</string>
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">Nastavení</string>
|
||||
<string name="app_settings_user_interface">Uživatelské prostředí</string>
|
||||
<string name="app_settings_reset_hints">Resetovat nápovědu</string>
|
||||
<string name="app_settings_reset_hints_summary">Znovu povolí vypnuté texty nápovědy</string>
|
||||
<string name="app_settings_reset_hints_success">Budou zobrazovány všechny texty nápovědy</string>
|
||||
<string name="app_settings_connection">Připojení</string>
|
||||
<string name="app_settings_override_proxy">Přepsat proxy nastavení</string>
|
||||
<string name="app_settings_override_proxy_on">Použít vlastní proxy nastavení</string>
|
||||
<string name="app_settings_override_proxy_off">Použít výchozí systémová proxy nastavení</string>
|
||||
<string name="app_settings_override_proxy_host">HTTP proxy hostname</string>
|
||||
<string name="app_settings_override_proxy_port">HTTP proxy port</string>
|
||||
<string name="app_settings_security">Zabezpečení</string>
|
||||
<string name="app_settings_distrust_system_certs">Nedůvěřovat systémovým certifikátům</string>
|
||||
<string name="app_settings_distrust_system_certs_on">Systémovým a uživatelem přidaným CA nebude důvěřováno</string>
|
||||
<string name="app_settings_distrust_system_certs_off">Systémovým a uživatelem přidaným CA bude důvěřováno (doporučeno)</string>
|
||||
<string name="app_settings_reset_certificates">Resetovat (ne)důvěryhodné certifikáty</string>
|
||||
<string name="app_settings_reset_certificates_summary">Resetovat důvěryhodnost všech vlastních certifikátů</string>
|
||||
<string name="app_settings_reset_certificates_success">Všechny vlastní certifikáty byly resetovány</string>
|
||||
<string name="app_settings_debug">Ladění</string>
|
||||
<string name="app_settings_log_to_external_storage">Logovat do externího souboru</string>
|
||||
<string name="app_settings_log_to_external_storage_on">Logování do externího úložiště (pokud dostupné)</string>
|
||||
<string name="app_settings_log_to_external_storage_off">Logování do externího souboru je vypnuto</string>
|
||||
<string name="app_settings_show_debug_info">Zobrazit ladící informace</string>
|
||||
<string name="app_settings_show_debug_info_details">Zobrazit/sdílet software a detaily konfigurace</string>
|
||||
<!--AccountActivity-->
|
||||
<string name="account_synchronize_now">Synchronizovat nyní</string>
|
||||
<string name="account_synchronizing_now">Probíhá synchronizace</string>
|
||||
<string name="account_settings">Nastavení účtu</string>
|
||||
<string name="account_rename">Přejmenovat účet</string>
|
||||
<string name="account_rename_new_name">Neuložená místní data mohou být vynechána. Po přejmenování je vyžadována nová synchronizace. Nové jméno účtu:</string>
|
||||
<string name="account_rename_rename">Přejmenovat</string>
|
||||
<string name="account_delete">Smazat účet</string>
|
||||
<string name="account_delete_confirmation_title">Opravdu smazat účet?</string>
|
||||
<string name="account_delete_confirmation_text">Všechny místní kopie adresáře, kalendářů a úkolů budou smazány.</string>
|
||||
<string name="account_refresh_address_book_list">Obnovit seznam adresářů</string>
|
||||
<string name="account_create_new_address_book">Vytvořit nový adresář</string>
|
||||
<string name="account_refresh_calendar_list">Obnovit seznam kalendářů</string>
|
||||
<string name="account_create_new_calendar">Vytvořit nový kalendář</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">DAVdroid oprávnění</string>
|
||||
<string name="permissions_calendar">Oprávnění pro kalendáře</string>
|
||||
<string name="permissions_calendar_details">Pro synchronizaci CalDAV událostí s místním kalendářem potřebuje DAVdroid oprávnění přistupovat ke kalendářům.</string>
|
||||
<string name="permissions_calendar_request">Vyžádat oprávnění kalendáře</string>
|
||||
<string name="permissions_contacts">Oprávnění pro kontakty</string>
|
||||
<string name="permissions_contacts_details">Pro synchronizaci CardDAV adresářů s místními kontakty potřebuje DAVdroid oprávnění přistupovat ke kontaktům.</string>
|
||||
<string name="permissions_contacts_request">Vyžádat oprávnění kontaktů</string>
|
||||
<string name="permissions_opentasks">Oprávnění pro OpenTasks</string>
|
||||
<string name="permissions_opentasks_details">Pro synchronizaci CalDAV událostí s místním seznamem úkolů potřebuje DAVdroid oprávnění přistupovat k OpenTasks.</string>
|
||||
<string name="permissions_opentasks_request">Vyžádat oprávnění OpenTasks</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">Přidat účet</string>
|
||||
<string name="login_type_email">Přihlášení s emailovou adresou</string>
|
||||
<string name="login_email_address">Emailová adresa</string>
|
||||
<string name="login_email_address_error">Vyžadován platný email</string>
|
||||
<string name="login_password">Heslo</string>
|
||||
<string name="login_password_required">Vyžadováno heslo</string>
|
||||
<string name="login_type_url">Přihlášení s URL a uživatelským jménem</string>
|
||||
<string name="login_url_must_be_http_or_https">URL musí začínat na http(s)://</string>
|
||||
<string name="login_url_host_name_required">Vyžadováno hostname</string>
|
||||
<string name="login_user_name">Uživatelské jméno</string>
|
||||
<string name="login_user_name_required">Vyžadováno uživatelské jméno</string>
|
||||
<string name="login_base_url">Základní URL</string>
|
||||
<string name="login_login">Login</string>
|
||||
<string name="login_back">Zpět</string>
|
||||
<string name="login_create_account">Vytvořit účet</string>
|
||||
<string name="login_account_name">Jméno účtu</string>
|
||||
<string name="login_account_name_info">Pro jméno účtu použijte svou emailovou adresu, protože Android bude brát jméno účtu jako údaj pro ORGANIZÁTORA vytvořených událostí. Nelze mít dva účty stejného jména.</string>
|
||||
<string name="login_account_contact_group_method">Metoda seskupování kontaktů:</string>
|
||||
<string name="login_account_name_required">Vyžadováno jméno účtu</string>
|
||||
<string name="login_account_not_created">Účet nelze vytvořit</string>
|
||||
<string name="login_configuration_detection">Vyhledání konfigurace</string>
|
||||
<string name="login_querying_server">Chvíli strpení, probíhá dotazování serveru...</string>
|
||||
<string name="login_no_caldav_carddav">Nelze nalézt službu CalDAV nebo CardDAV.</string>
|
||||
<string name="login_view_logs">Prohlížet logy</string>
|
||||
<!--AccountSettingsActivity-->
|
||||
<string name="settings_title">Nastavení: %s</string>
|
||||
<string name="settings_authentication">Ověření</string>
|
||||
<string name="settings_username">Uživatelské jméno</string>
|
||||
<string name="settings_enter_username">Zadat uživatelské jméno</string>
|
||||
<string name="settings_password">Heslo</string>
|
||||
<string name="settings_password_summary">Aktualizovat heslo dle svého serveru.</string>
|
||||
<string name="settings_enter_password">Vložit své heslo:</string>
|
||||
<string name="settings_sync">Synchronizace</string>
|
||||
<string name="settings_sync_interval_contacts">Interval synchronizace kontaktů</string>
|
||||
<string name="settings_sync_summary_manually">Pouze manuálně</string>
|
||||
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Každých %d minut a ihned při lokálních změnách</string>
|
||||
<string name="settings_sync_interval_calendars">Interval synchronizace kalendáře</string>
|
||||
<string name="settings_sync_interval_tasks">Interval synchronizace úkolů</string>
|
||||
<string name="settings_sync_wifi_only">Synchronizovat pouze přes WiFi</string>
|
||||
<string name="settings_sync_wifi_only_on">Synchronizace omezena na WiFi připojení</string>
|
||||
<string name="settings_sync_wifi_only_off">Druh připojení není brán v potaz</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">Metoda seskupování kontaktů</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string-array name="settings_contact_group_method_entries">
|
||||
<item>Skupiny jsou oddělené soubory VCard</item>
|
||||
<item>Skupiny jsou kategorie na kontakt</item>
|
||||
</string-array>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Časový limit pro staré události</string>
|
||||
<string name="settings_sync_time_range_past_none">Synchronizovat všechny události</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="one">Ignorovat události starší než 1 den</item>
|
||||
<item quantity="few">Ignorovat události starší než %d dny</item>
|
||||
<item quantity="other">Ignorovat události starší než %d dnů</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Události z minulosti starší než vyznačený počet dnů budou ignorovány (lze zadat 0). Ponechte prázdné pro synchronizaci všech událostí.</string>
|
||||
<string name="settings_manage_calendar_colors">Spravovat barvy kalendářů</string>
|
||||
<string name="settings_manage_calendar_colors_on">Barvy kalendářů spravuje DAVdroid</string>
|
||||
<string name="settings_manage_calendar_colors_off">Barvy kalendářů nespravuje DAVdroid</string>
|
||||
<!--collection management-->
|
||||
<string name="create_addressbook">Vytvořit adresář</string>
|
||||
<string name="create_addressbook_display_name_hint">Můj adresář</string>
|
||||
<string name="create_calendar">Vytvořit CalDAV sbírku</string>
|
||||
<string name="create_calendar_display_name_hint">Můj kalendář</string>
|
||||
<string name="create_calendar_time_zone">Časová zóna:</string>
|
||||
<string name="create_calendar_type">Typ sbírky:</string>
|
||||
<string name="create_calendar_type_only_events">Kalendář (pouze události)</string>
|
||||
<string name="create_calendar_type_only_tasks">Seznam úkolů (pouze úkoly)</string>
|
||||
<string name="create_calendar_type_events_and_tasks">Kombinovaná (události a úkoly)</string>
|
||||
<string name="create_collection_color">Nastavit barvu sbírky</string>
|
||||
<string name="create_collection_creating">Vytváření sbírky</string>
|
||||
<string name="create_collection_display_name">Zobrazit jméno (nadpis) této sbírky:</string>
|
||||
<string name="create_collection_display_name_required">Nadpis je vyžadován</string>
|
||||
<string name="create_collection_description">Popis (volitelný):</string>
|
||||
<string name="create_collection_home_set">Domácí sbírka:</string>
|
||||
<string name="create_collection_create">Vytvořit</string>
|
||||
<string name="delete_collection">Smazat sbírku</string>
|
||||
<string name="delete_collection_confirm_title">Jste si jisti?</string>
|
||||
<string name="delete_collection_confirm_warning">Tato sbírka (%s) a všechna její data budou odstraněna ze serveru.</string>
|
||||
<string name="delete_collection_deleting_collection">Mazání sbírky</string>
|
||||
<!--ExceptionInfoFragment-->
|
||||
<string name="exception">Došlo k chybě.</string>
|
||||
<string name="exception_httpexception">Došlo k HTTP chybě.</string>
|
||||
<string name="exception_ioexception">Došlo k I/O chybě.</string>
|
||||
<string name="exception_show_details">Zobrazit detaily</string>
|
||||
<!--sync adapters and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Ladící informace</string>
|
||||
<string name="sync_error_permissions">DAVdroid oprávnění</string>
|
||||
<string name="sync_error_permissions_text">Vyžadována dodatečná oprávnění</string>
|
||||
<string name="sync_error_calendar">Synchronizace kalendáře selhala (%s)</string>
|
||||
<string name="sync_error_contacts">Synchronizace adresáře selhala (%s)</string>
|
||||
<string name="sync_error_tasks">Synchronizace úkolu selhala (%s)</string>
|
||||
<string name="sync_error">Chyba při %s</string>
|
||||
<string name="sync_error_http_dav">Chyba serveru při %s</string>
|
||||
<string name="sync_error_local_storage">Chyba databáze při %s</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>příprava synchronizace</item>
|
||||
<item>dotazování možností</item>
|
||||
<item>zpracovávání místně smazaných záznamů</item>
|
||||
<item>příprava vytvořených/upravených záznamů</item>
|
||||
<item>nahrávání vytvořených/upravených záznamů</item>
|
||||
<item>kontrola stavu synchronizace</item>
|
||||
<item>výpis místních záznamů</item>
|
||||
<item>výpis vzdálených záznamů</item>
|
||||
<item>porovnání místních/vzdálených záznamů</item>
|
||||
<item>stahování vzdálených záznamů</item>
|
||||
<item>uzavírání procesu</item>
|
||||
<item>ukládání stavu synchronizace</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">Chybné uživatelské jméno/heslo</string>
|
||||
<!--cert4android-->
|
||||
<string name="certificate_notification_connection_security">DAVdroid: Zabezpečení připojení</string>
|
||||
<string name="trust_certificate_unknown_certificate_found">DAVdroid nalezl neznámý certifikát. Chcete mu důvěřovat?</string>
|
||||
</resources>
|
||||
219
app/src/davdroid/res/values-da/strings.xml
Normal file
219
app/src/davdroid/res/values-da/strings.xml
Normal file
@@ -0,0 +1,219 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!--common strings-->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="account_title_address_book">DAVdroid Addressebog</string>
|
||||
<string name="address_books_authority_title">Adressebøger</string>
|
||||
<string name="help">Hjælp</string>
|
||||
<string name="manage_accounts">Administrer konti</string>
|
||||
<string name="please_wait">Vent venligst ...</string>
|
||||
<string name="send">Send</string>
|
||||
<!--startup dialogs-->
|
||||
<string name="startup_battery_optimization">Batterioptimering</string>
|
||||
<string name="startup_battery_optimization_message">Android kan deaktivere/reducere DAVDroid synkronisering efter et par dage. For at undgå dette, slå batterioptimering fra.</string>
|
||||
<string name="startup_battery_optimization_disable">Deaktiver DAVdroid</string>
|
||||
<string name="startup_dont_show_again">Vis ikke igen</string>
|
||||
<string name="startup_donate">Open-Source Information</string>
|
||||
<string name="startup_donate_message">Det glæder os, at du bruger DAVdroid, som er open source-software (GPLv3). Det er hårdt arbejde at udvikle DAVdroid og taget tusindvis af arbejdstimer, så overvej at donere til projektet.</string>
|
||||
<string name="startup_donate_now">Vis donationsside</string>
|
||||
<string name="startup_donate_later">Måske senere</string>
|
||||
<string name="startup_google_play_accounts_removed">Play Store DRM-fejl: Information</string>
|
||||
<string name="startup_google_play_accounts_removed_message">Under visse tekniske omstændigheder kan DRM fra Play Store bevirke, at alle DAVdroid-konti er væk efter en genstart eller opgradering af DAVdroid. Hvis du er udsat for dette problem (og ellers ikke), opfordres du til at installere DAVDroid JB Workaround\" fra Play Store.</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Yderligere oplysninger</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks ikke installeret</string>
|
||||
<string name="startup_opentasks_not_installed_message">OpenTasks er ikke til rådighedm så DAVdroid vil ikke kunne synkronisere opgavelister.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Efter at have installeret OpenTasks, vil du være nødt til at GENINSTALLERE DAVdroid og dine konti igen (en fejl i Android).</string>
|
||||
<string name="startup_opentasks_not_installed_install">Installer OpenTasks</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Licensforhold</string>
|
||||
<string name="about_license_info_no_warranty">Dette program leveres ABSOLUT UDEN GARANTI. Det er fri software, og du er velkommen til at videredistribuere det under visse betingelse.</string>
|
||||
<!--global settings-->
|
||||
<string name="logging_davdroid_file_logging">DAVdroid fil-logning</string>
|
||||
<string name="logging_to_external_storage">Logger til eksternt datalager: %s</string>
|
||||
<string name="logging_couldnt_create_file">Kunne ikke oprette ekstern logfil: %s</string>
|
||||
<string name="logging_no_external_storage">Eksternt lager ikke funder</string>
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_open">Åbn navigationsvindue</string>
|
||||
<string name="navigation_drawer_close">Luk navigationsvindue</string>
|
||||
<string name="navigation_drawer_subtitle">CalDAV/CardDAV Sync-adapter</string>
|
||||
<string name="navigation_drawer_about">Om / Licens</string>
|
||||
<string name="navigation_drawer_settings">Indstillinger</string>
|
||||
<string name="navigation_drawer_news_updates">Nyheder & opdateringer</string>
|
||||
<string name="navigation_drawer_external_links">Eksterne links</string>
|
||||
<string name="navigation_drawer_website">Hjemmeside</string>
|
||||
<string name="navigation_drawer_faq">FAQ</string>
|
||||
<string name="navigation_drawer_donate">Donation</string>
|
||||
<string name="account_list_empty">Velkommen til DAVdroid!\n\nDu kan nu tilføje en CaDAV/CardDAV-konto.</string>
|
||||
<string name="accounts_global_sync_disabled">Automatisk synkronisering på tværs af systemet er deaktiveret</string>
|
||||
<string name="accounts_global_sync_enable">Aktiver</string>
|
||||
<!--DavService-->
|
||||
<string name="dav_service_refresh_failed">Registrering af tjeneste kunne ikke foretages</string>
|
||||
<string name="dav_service_refresh_couldnt_refresh">Kunne opdatere liste over sæt</string>
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">Indstillinger</string>
|
||||
<string name="app_settings_user_interface">Brugerflade</string>
|
||||
<string name="app_settings_reset_hints">Nulstil vejledende popups</string>
|
||||
<string name="app_settings_reset_hints_summary">Genaktiverer hjælp, som er blevet lukket tidligere</string>
|
||||
<string name="app_settings_reset_hints_success">Al vejledning vil blive vist igen</string>
|
||||
<string name="app_settings_connection">Forbindelse</string>
|
||||
<string name="app_settings_override_proxy">Tilsidesæt proxyindstillinger</string>
|
||||
<string name="app_settings_override_proxy_on">Brug brugerdefinerede proxyindstillinger</string>
|
||||
<string name="app_settings_override_proxy_off">Brug systemstandard proxyindstillinger</string>
|
||||
<string name="app_settings_override_proxy_host">HTTP proxy værtsnavn</string>
|
||||
<string name="app_settings_override_proxy_port">HTTP proxy port</string>
|
||||
<string name="app_settings_security">Sikkerhed</string>
|
||||
<string name="app_settings_distrust_system_certs">Stol ikke på systemcertifikater</string>
|
||||
<string name="app_settings_distrust_system_certs_on">System og brugertilføjede CA\'er vil ikke blive betroet</string>
|
||||
<string name="app_settings_distrust_system_certs_off">System og brugertilføjede CA\'er vil blive betroet (anbefalet)</string>
|
||||
<string name="app_settings_reset_certificates">Nulstil (ikke-)betroede certifikater</string>
|
||||
<string name="app_settings_reset_certificates_summary">Nulstiller tilliden til brugerdefinerede certifikater</string>
|
||||
<string name="app_settings_reset_certificates_success">Alle brugerdefinerede certifikater er blevet rydet</string>
|
||||
<string name="app_settings_debug">Debugging</string>
|
||||
<string name="app_settings_log_to_external_storage">Log til ekstern fil</string>
|
||||
<string name="app_settings_log_to_external_storage_on">Logger til eksternt lager (hvis muligt)</string>
|
||||
<string name="app_settings_log_to_external_storage_off">Ekstern logning deaktiveret</string>
|
||||
<string name="app_settings_show_debug_info">Vis debug-info</string>
|
||||
<string name="app_settings_show_debug_info_details">Vis/del software og opsætningsoplysninger</string>
|
||||
<!--AccountActivity-->
|
||||
<string name="account_synchronize_now">Synkroniser nu</string>
|
||||
<string name="account_synchronizing_now">Synkroniserer nu</string>
|
||||
<string name="account_settings">Opsætning af konti</string>
|
||||
<string name="account_rename">Omdøb konto</string>
|
||||
<string name="account_rename_new_name">Lokaldata der ikke er gemt kan gå tabt. Eftersynkronisering er krævet efter omdøbning. Nyt kontonavn: </string>
|
||||
<string name="account_rename_rename">Omdøb</string>
|
||||
<string name="account_delete">Slet konto</string>
|
||||
<string name="account_delete_confirmation_title">Ønsker du at slette konto?</string>
|
||||
<string name="account_delete_confirmation_text">Alle lokale kopier af addessebøger, kalendere og opgavelister vil blive slettet.</string>
|
||||
<string name="account_refresh_address_book_list">Opdater adressebogslister</string>
|
||||
<string name="account_create_new_address_book">Opret ny adressebog</string>
|
||||
<string name="account_refresh_calendar_list">Opdater kalenderliste</string>
|
||||
<string name="account_create_new_calendar">Opret ny kalender</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">DAVdroid adgangsrettigheder</string>
|
||||
<string name="permissions_calendar">Kalenderadgange</string>
|
||||
<string name="permissions_calendar_details">For at synkronisere CalDAV-begivenheder med dine lokale kalendere, skal DAVdroid have adgang til dine kalendere.</string>
|
||||
<string name="permissions_calendar_request">Anmod om kalenderadgang</string>
|
||||
<string name="permissions_contacts">Kontakter: Adgangsrettigheder</string>
|
||||
<string name="permissions_contacts_details">DAVdroid er nødt til at have adgang til dine kontakter, hvis CardDAV-adressebøger skal kunne synkronisere med dine kontakter.</string>
|
||||
<string name="permissions_contacts_request">Anmod om adgang til kontakter</string>
|
||||
<string name="permissions_opentasks">OpenTasks: Adgangsrettigheder</string>
|
||||
<string name="permissions_opentasks_details">DAVdroid er nødt til at have adgang til OpenTasks, hvis CalDAV-opgaver skal kunne synkronisere med dine lokale opgavelister.</string>
|
||||
<string name="permissions_opentasks_request">Anmod om adgang til OpenTasks</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">Tilføj konto</string>
|
||||
<string name="login_type_email">Log ind med emailadresse</string>
|
||||
<string name="login_email_address">Emailadresse</string>
|
||||
<string name="login_email_address_error">Gyldig emailadresse påkrævet</string>
|
||||
<string name="login_password">Adgangskode</string>
|
||||
<string name="login_password_required">Adgangskode påkrævet</string>
|
||||
<string name="login_type_url">Log ind med URL og brugernavn</string>
|
||||
<string name="login_url_must_be_http_or_https">URL skal begynde med http(s)://</string>
|
||||
<string name="login_url_host_name_required">Værtsnavn påkrævet</string>
|
||||
<string name="login_user_name">Brugernavn</string>
|
||||
<string name="login_user_name_required">Brugernavn påkrævet</string>
|
||||
<string name="login_base_url">Basis-URL</string>
|
||||
<string name="login_login">Login</string>
|
||||
<string name="login_back">Tilbage</string>
|
||||
<string name="login_create_account">Opret konto</string>
|
||||
<string name="login_account_name">Kontonavn</string>
|
||||
<string name="login_account_name_info">Brug emailadressen som kontonavn, for Android vil bruge kontonavnet til ORGANIZER-feltet for aktiviteter, som du opretter. Du kan ikke have to konti med samme navn.</string>
|
||||
<string name="login_account_contact_group_method">Gruppering af kontakter:</string>
|
||||
<string name="login_account_name_required">Kontonavn påkrævet</string>
|
||||
<string name="login_account_not_created">Konto kunne ikke oprettes</string>
|
||||
<string name="login_configuration_detection">Check konfiguration</string>
|
||||
<string name="login_querying_server">Vent, forespørger hos serveren...</string>
|
||||
<string name="login_no_caldav_carddav">Kunne ikke finde CalDAV- eller CardDAV-tjeneste.</string>
|
||||
<string name="login_view_logs">Vis logs.</string>
|
||||
<!--AccountSettingsActivity-->
|
||||
<string name="settings_title">Indstillinger: %s</string>
|
||||
<string name="settings_authentication">Adgangsgodkendelse</string>
|
||||
<string name="settings_username">Brugernavn</string>
|
||||
<string name="settings_enter_username">Indtast brugernavn:</string>
|
||||
<string name="settings_password">Adgangskode</string>
|
||||
<string name="settings_password_summary">Opdater adgangskoden, så den svarer til din server.</string>
|
||||
<string name="settings_enter_password">Indtast adgangskode:</string>
|
||||
<string name="settings_sync">Synkronisering</string>
|
||||
<string name="settings_sync_interval_contacts">Synkroniseringsinterval for kontakter</string>
|
||||
<string name="settings_sync_summary_manually">Kun manuelt</string>
|
||||
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Hver %d minutter + øjeblikkeligt ved lokale ændringer</string>
|
||||
<string name="settings_sync_interval_calendars">Synkroniseringsinterval for kalender</string>
|
||||
<string name="settings_sync_interval_tasks">Synkroniseringsinterval for opgaver</string>
|
||||
<string name="settings_sync_wifi_only">Synkroniser kun over WiFi</string>
|
||||
<string name="settings_sync_wifi_only_on">Synkronisering er begrænset til WiFi-forbindelser</string>
|
||||
<string name="settings_sync_wifi_only_off">Forbindelsestypen har ingen betydning</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">Gruppering af kontakter</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string-array name="settings_contact_group_method_entries">
|
||||
<item>Grupper er særskilte VCards</item>
|
||||
<item>Grupper er kategorier pr. kontakt</item>
|
||||
</string-array>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Tidsafgrænsning for tidligere begivenheder</string>
|
||||
<string name="settings_sync_time_range_past_none">Alle begivenheder vil blive synkroniseret</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="one">Begivenheder ældre end en dag vil blive ignoreret</item>
|
||||
<item quantity="other">Begivenheder, der er mere end %d dage gamle, vil blive ignoreret</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Begivenheder, som er mere end dette antal dage gamle vil blive ignoreret (kan også være 0). Hvis feltet ikke er udfyldt, vil alle begivenheder blive synkroniseret.</string>
|
||||
<string name="settings_manage_calendar_colors">Administrer farver for kalender</string>
|
||||
<string name="settings_manage_calendar_colors_on">Kalenderfarver administreres af DAVdroid</string>
|
||||
<string name="settings_manage_calendar_colors_off">Kalenderfarver sættes ikke fra DAVdroid</string>
|
||||
<!--collection management-->
|
||||
<string name="create_addressbook">Opret adressebog</string>
|
||||
<string name="create_addressbook_display_name_hint">Min adressebog</string>
|
||||
<string name="create_calendar">Opret CalDAV-sæt</string>
|
||||
<string name="create_calendar_display_name_hint">Min kalender</string>
|
||||
<string name="create_calendar_time_zone">Tidszone:</string>
|
||||
<string name="create_calendar_type">Type af sæt:</string>
|
||||
<string name="create_calendar_type_only_events">Kalender (kun begivenheder)</string>
|
||||
<string name="create_calendar_type_only_tasks">Opgaveliste (kun opgaver)</string>
|
||||
<string name="create_calendar_type_events_and_tasks">Kombineret (begivenheder og opgaver)</string>
|
||||
<string name="create_collection_color">Sæt en farve på sættet</string>
|
||||
<string name="create_collection_creating">Opretter sæt</string>
|
||||
<string name="create_collection_display_name">Navn (titel) på dette sæt:</string>
|
||||
<string name="create_collection_display_name_required">En titel er påkrævet</string>
|
||||
<string name="create_collection_description">Beskrivelse (valgfri):</string>
|
||||
<string name="create_collection_home_set">Home set:</string>
|
||||
<string name="create_collection_create">Opret</string>
|
||||
<string name="delete_collection">Slet sæt</string>
|
||||
<string name="delete_collection_confirm_title">Er du sikker?</string>
|
||||
<string name="delete_collection_confirm_warning">DAV-sættet (%s) og dets data vil blive fjernet fra serveren.</string>
|
||||
<string name="delete_collection_deleting_collection">Sletter CalDAV-sæt</string>
|
||||
<!--ExceptionInfoFragment-->
|
||||
<string name="exception">Der er opstået en fejl.</string>
|
||||
<string name="exception_httpexception">Der er opstået en HTTP-fejl.</string>
|
||||
<string name="exception_ioexception">Der er opstået en I/O-fejl.</string>
|
||||
<string name="exception_show_details">Vis detaljer</string>
|
||||
<!--sync adapters and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Debug-info</string>
|
||||
<string name="sync_error_permissions">DAVdroid-rettigheder</string>
|
||||
<string name="sync_error_permissions_text">Yderligere adgang påkrævet</string>
|
||||
<string name="sync_error_calendar">Synkronisering af kalenderen lykkedes ikke (%s)</string>
|
||||
<string name="sync_error_contacts">Synkronisering af adressebogen lykkedes ikke (%s)</string>
|
||||
<string name="sync_error_tasks">Synkronisering af opgaver lykkedes ikke (%s)</string>
|
||||
<string name="sync_error">Fejl under %s</string>
|
||||
<string name="sync_error_http_dav">Serverfejl under %s</string>
|
||||
<string name="sync_error_local_storage">Databasefejl under %s</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>forbereder synkronisering</item>
|
||||
<item>checker understøttelse</item>
|
||||
<item>behandler poster, der er slettet lokalt</item>
|
||||
<item>behandler poster, der er blevet oprettet/redigeret</item>
|
||||
<item>uploader poster, der er blevet oprettet/redigeret</item>
|
||||
<item>checker synkroniseringsstatus</item>
|
||||
<item>laver liste af lokale poster</item>
|
||||
<item>laver lister af poster på server</item>
|
||||
<item>sammenligner poster lokalt og på server</item>
|
||||
<item>downloader poster på server</item>
|
||||
<item>efterbehandler</item>
|
||||
<item>gemmer synkroniseringsstatus</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">Fejl i brugernavn/adgangskode</string>
|
||||
<!--cert4android-->
|
||||
<string name="certificate_notification_connection_security">DAVdroid: Forbindelsessikkerhed</string>
|
||||
<string name="trust_certificate_unknown_certificate_found">DAVdroid er stødt på et ukendt certifikat. Vil du stole på det? </string>
|
||||
</resources>
|
||||
215
app/src/davdroid/res/values-es/strings.xml
Normal file
215
app/src/davdroid/res/values-es/strings.xml
Normal file
@@ -0,0 +1,215 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!--common strings-->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="help">Ayuda</string>
|
||||
<string name="manage_accounts">Administrar cuentas</string>
|
||||
<string name="please_wait">Por favor, espere...</string>
|
||||
<string name="send">Enviar</string>
|
||||
<!--startup dialogs-->
|
||||
<string name="startup_battery_optimization">Optimización de batería</string>
|
||||
<string name="startup_battery_optimization_message">Android puede desactivar/reducir la sincronización de DAVdroid después de unos días. Para prevenir esto, desactiva la optimización.</string>
|
||||
<string name="startup_battery_optimization_disable">Apagar para DAVdroid</string>
|
||||
<string name="startup_dont_show_again">No mostrar de nuevo</string>
|
||||
<string name="startup_donate">Información de código abierto</string>
|
||||
<string name="startup_donate_message">Nos alegra que uses DAVdroid, que es software de código abierto (GPLv3). Debido al duro trabajo que supone el desarrollo de DAVdroid y los cientos de horas de trabajo, por favor, considera hacer una donación.</string>
|
||||
<string name="startup_donate_now">Mostrar página de donación</string>
|
||||
<string name="startup_donate_later">Quizás luego</string>
|
||||
<string name="startup_google_play_accounts_removed">Información de error de DRM de Play Store</string>
|
||||
<string name="startup_google_play_accounts_removed_message">Bajo ciertas condiciones, el DRM de Play Store puede causar que todas las cuentas de DAVdroid se desconfiguren tras un reinicio o una actualización de DAVdroid. Si esto le afecta (y sólo en ese caso), por favor, instale \"DAVdroid JB Workaround\" desde Play Store.</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Más información</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks no está instalado</string>
|
||||
<string name="startup_opentasks_not_installed_message">La aplicación OpenTasks no está disponible. DAVdroid no podrá sincronizar listas de tareas.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Tras instalar OpenTasks, tendrás que re-instalar DAVdroid y añadir tus cuentas de nuevo (por un error de Android).</string>
|
||||
<string name="startup_opentasks_not_installed_install">Instalar OpenTasks</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Términos de la licencia</string>
|
||||
<string name="about_license_info_no_warranty">Este programa viene sin NINGÚN TIPO DE GARANTÍA. Es software libre, y cualquier contribución es bienvenida y redistribuida bajo ciertas condiciones.</string>
|
||||
<!--global settings-->
|
||||
<string name="logging_davdroid_file_logging">Archivo de registro de DAVdroid</string>
|
||||
<string name="logging_to_external_storage">Registrar en almacenamiento externo: %s</string>
|
||||
<string name="logging_couldnt_create_file">No se puede crear el archivo de registro externo: %s</string>
|
||||
<string name="logging_no_external_storage">Almacenamiento externo no encontrado</string>
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_open">Abrir panel de navegación</string>
|
||||
<string name="navigation_drawer_close">Cerrar panel de navegación</string>
|
||||
<string name="navigation_drawer_subtitle">Adaptador de sincronización CalDAV/CardDAV</string>
|
||||
<string name="navigation_drawer_about">Acerca de / Licencia</string>
|
||||
<string name="navigation_drawer_settings">Ajustes</string>
|
||||
<string name="navigation_drawer_news_updates">Noticias y actualizaciones</string>
|
||||
<string name="navigation_drawer_external_links">Enlaces externos</string>
|
||||
<string name="navigation_drawer_website">Sitio web</string>
|
||||
<string name="navigation_drawer_faq">Preguntas frequentes</string>
|
||||
<string name="navigation_drawer_donate">Donar</string>
|
||||
<string name="account_list_empty">Bienvenido a DAVdroid!\n\nAhora puedes añadir una cuenta CalDAV/CardDAV.</string>
|
||||
<!--DavService-->
|
||||
<string name="dav_service_refresh_failed">Falló la detección del servicio</string>
|
||||
<string name="dav_service_refresh_couldnt_refresh">No se pudo refrescar lista de colección</string>
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">Ajustes</string>
|
||||
<string name="app_settings_user_interface">Interfaz de usuario</string>
|
||||
<string name="app_settings_reset_hints">Restablecer advertencias</string>
|
||||
<string name="app_settings_reset_hints_summary">Habilita las advertencias que han sido rechazadas con anterioridad</string>
|
||||
<string name="app_settings_reset_hints_success">Todas las advertencias se mostrarán nuevamente</string>
|
||||
<string name="app_settings_connection">Conexión</string>
|
||||
<string name="app_settings_override_proxy">Anular ajustes del proxy</string>
|
||||
<string name="app_settings_override_proxy_on">Usar ajustes personalizados del proxy</string>
|
||||
<string name="app_settings_override_proxy_off">Usar ajustes del proxy predefinidos por el sistema</string>
|
||||
<string name="app_settings_override_proxy_host">Nombre del host del proxy HTTP</string>
|
||||
<string name="app_settings_override_proxy_port">Puerto del proxy HTTP</string>
|
||||
<string name="app_settings_security">Seguridad</string>
|
||||
<string name="app_settings_distrust_system_certs">Invalidar los certificados del sistema</string>
|
||||
<string name="app_settings_distrust_system_certs_on">Los CA del sistema y los añadidos por el usuario no serán válidos</string>
|
||||
<string name="app_settings_distrust_system_certs_off">Los CA del sistema y los añadidos por el usuario serán usados y de confianza (recomendado)</string>
|
||||
<string name="app_settings_reset_certificates">Reiniciar certificados (in)validados</string>
|
||||
<string name="app_settings_reset_certificates_summary">Reinicia la validez de todos los certificados particulares</string>
|
||||
<string name="app_settings_reset_certificates_success">Todos los certificados particulares han sido limpiados</string>
|
||||
<string name="app_settings_debug">Depuración</string>
|
||||
<string name="app_settings_log_to_external_storage">Registrar en fichero externo</string>
|
||||
<string name="app_settings_log_to_external_storage_on">Registro en almacenamiento externo (si está disponible)</string>
|
||||
<string name="app_settings_log_to_external_storage_off">El archivo de registro externo está deshabilitado</string>
|
||||
<string name="app_settings_show_debug_info">Mostrar la información de depuración</string>
|
||||
<string name="app_settings_show_debug_info_details">Ver/compartir detalles de software y configuración</string>
|
||||
<!--AccountActivity-->
|
||||
<string name="account_synchronize_now">Sincronizar ahora</string>
|
||||
<string name="account_synchronizing_now">Sincronizando...</string>
|
||||
<string name="account_settings">Ajustes de cuenta</string>
|
||||
<string name="account_rename">Renombrar cuenta</string>
|
||||
<string name="account_rename_new_name">Información local no guardada puede ser desechada. Se requiere resincronizar después de renombrar. Nuevo nombre de cuenta:</string>
|
||||
<string name="account_rename_rename">Renombrar</string>
|
||||
<string name="account_delete">Eliminar cuenta</string>
|
||||
<string name="account_delete_confirmation_title">¿Seguro que deseas eliminar la cuenta?</string>
|
||||
<string name="account_delete_confirmation_text">Todas las copias locales de tus contactos, calendarios y tareas serán eliminadas.</string>
|
||||
<string name="account_refresh_address_book_list">Refrescar contactos</string>
|
||||
<string name="account_create_new_address_book">Crear nueva lista de contactos</string>
|
||||
<string name="account_refresh_calendar_list">Refrescar calendario</string>
|
||||
<string name="account_create_new_calendar">Crear nuevo calendario</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">Permisos de DAVdroid</string>
|
||||
<string name="permissions_calendar">Permisos de calendario</string>
|
||||
<string name="permissions_calendar_details">Para sincronizar eventos CalDAV con tus calendarios locales, DAVdroid necesita acceder a los mismos.</string>
|
||||
<string name="permissions_calendar_request">Solicitar permisos sobre calendario</string>
|
||||
<string name="permissions_contacts">Permisos de contactos</string>
|
||||
<string name="permissions_contacts_details">Para sincronizar libretas de contactos CadDAV con tus contactos locales, DAVdroid necesita acceder a los mismos.</string>
|
||||
<string name="permissions_contacts_request">Solicitar permisos sobre contactos</string>
|
||||
<string name="permissions_opentasks">Permisos de OpenTasks</string>
|
||||
<string name="permissions_opentasks_details">Para sincronizar listas de tareas CalDAV con tus listas de tareas locales, DAVdroid necesita acceder a OpenTasks.</string>
|
||||
<string name="permissions_opentasks_request">Solicitar permisos sobre OpenTasks</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">Añadir cuenta</string>
|
||||
<string name="login_type_email">Acceder con cuenta de correo</string>
|
||||
<string name="login_email_address">Dirección de correo</string>
|
||||
<string name="login_email_address_error">Se requiere una dirección de correo válida</string>
|
||||
<string name="login_password">Contraseña</string>
|
||||
<string name="login_password_required">Contraseña requerida</string>
|
||||
<string name="login_type_url">Acceder con URL y nombre de usuario</string>
|
||||
<string name="login_url_must_be_http_or_https">La URL debe comenzar con http(s)://</string>
|
||||
<string name="login_url_host_name_required">Nombre de servidor requerido</string>
|
||||
<string name="login_user_name">Nombre de usuario</string>
|
||||
<string name="login_user_name_required">Nombre de usuario requerido</string>
|
||||
<string name="login_base_url">URL base</string>
|
||||
<string name="login_login">Registrar</string>
|
||||
<string name="login_back">Volver</string>
|
||||
<string name="login_create_account">Crear cuenta</string>
|
||||
<string name="login_account_name">Nombre de cuenta</string>
|
||||
<string name="login_account_name_info">Usa tu dirección de correo como nombre de cuenta puesto que Android usará el nombre de la cuenta como campo de \"organizador\" en los eventos que cree. No puedes tener dos cuentas con el mismo nombre.</string>
|
||||
<string name="login_account_contact_group_method">Método de contacto de grupo:</string>
|
||||
<string name="login_account_name_required">Nombre de cuenta requerido</string>
|
||||
<string name="login_account_not_created">La cuenta no pudo ser creada</string>
|
||||
<string name="login_configuration_detection">Detectar configuración</string>
|
||||
<string name="login_querying_server">Por favor espera, consultando al servidor...</string>
|
||||
<string name="login_no_caldav_carddav">No se pudo encontrar el servicio CalDAV o CardDAV.</string>
|
||||
<string name="login_view_logs">Ver registros</string>
|
||||
<!--AccountSettingsActivity-->
|
||||
<string name="settings_title">Ajustes: %s</string>
|
||||
<string name="settings_authentication">Autenticación</string>
|
||||
<string name="settings_username">Nombre de usuario</string>
|
||||
<string name="settings_enter_username">Introduce tu nombre de usuario:</string>
|
||||
<string name="settings_password">Contraseña</string>
|
||||
<string name="settings_password_summary">Actualiza la contraseña de acuerdo a tu servidor.</string>
|
||||
<string name="settings_enter_password">Introduce tu contraseña:</string>
|
||||
<string name="settings_sync">Sincronización</string>
|
||||
<string name="settings_sync_interval_contacts">Intervalo de sincronización de contactos</string>
|
||||
<string name="settings_sync_summary_manually">Solo manualmente</string>
|
||||
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Cada %d minutos + inmediatamente con cambios locales</string>
|
||||
<string name="settings_sync_interval_calendars">Intervalo de sincronización de calendarios</string>
|
||||
<string name="settings_sync_interval_tasks">Intervalo de sincronizacion de Tasks</string>
|
||||
<string name="settings_sync_wifi_only">Sincronizar sólo sobre WiFi</string>
|
||||
<string name="settings_sync_wifi_only_on">La sincronización está restringida a conexiones WiFi</string>
|
||||
<string name="settings_sync_wifi_only_off">Tipo de conexión no tenido en cuenta</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">Método de contacto de grupo</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string-array name="settings_contact_group_method_entries">
|
||||
<item>Los groups tienen VCards separadas</item>
|
||||
<item>Los groups tienen una categoría por contacto</item>
|
||||
</string-array>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Límite de tiempo de eventos pasados</string>
|
||||
<string name="settings_sync_time_range_past_none">Todos los eventos serán sincronizados</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="one">Los eventos anteriores a un día serán ignorados</item>
|
||||
<item quantity="other">Los eventos anteriores a %d días serán ignorados</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Los eventos anteriores a este número de días serán ignorados (puede ser 0). Deja en blanco el campo para sincronizar todos los eventos.</string>
|
||||
<string name="settings_manage_calendar_colors">Colores de calendario</string>
|
||||
<string name="settings_manage_calendar_colors_on">Los colores de los calendarios son administrados por DAVdroid</string>
|
||||
<string name="settings_manage_calendar_colors_off">Los colores de los calendarios no son establecidos por DAVdroid</string>
|
||||
<!--collection management-->
|
||||
<string name="create_addressbook">Crear nueva lista de contactos</string>
|
||||
<string name="create_addressbook_display_name_hint">Agendas</string>
|
||||
<string name="create_calendar">Crear colección CalDAV</string>
|
||||
<string name="create_calendar_display_name_hint">Mi calendario</string>
|
||||
<string name="create_calendar_time_zone">Zona horaria:</string>
|
||||
<string name="create_calendar_type">Tipo de colección:</string>
|
||||
<string name="create_calendar_type_only_events">Calendario (sólo eventos)</string>
|
||||
<string name="create_calendar_type_only_tasks">Lista de tareas (sólo tareas)</string>
|
||||
<string name="create_calendar_type_events_and_tasks">Combinado (eventos y tareas)</string>
|
||||
<string name="create_collection_color">Establecer un color</string>
|
||||
<string name="create_collection_creating">Crear colección</string>
|
||||
<string name="create_collection_display_name">Nombre mostrado (título):</string>
|
||||
<string name="create_collection_display_name_required">Título requerido</string>
|
||||
<string name="create_collection_description">Descripción (opcional):</string>
|
||||
<string name="create_collection_home_set">Establecer localización:</string>
|
||||
<string name="create_collection_create">Crear</string>
|
||||
<string name="delete_collection">Eliminar colección</string>
|
||||
<string name="delete_collection_confirm_title">¿Estás seguro/a?</string>
|
||||
<string name="delete_collection_confirm_warning">Esta colección (%s) y toda su información será eliminada del servidor.</string>
|
||||
<string name="delete_collection_deleting_collection">Eliminando colección</string>
|
||||
<!--ExceptionInfoFragment-->
|
||||
<string name="exception">Ocurrió un error.</string>
|
||||
<string name="exception_httpexception">Ha ocurrido un error HTTP.</string>
|
||||
<string name="exception_ioexception">Ha ocurrido un error I/O.</string>
|
||||
<string name="exception_show_details">Mostrar detalles</string>
|
||||
<!--sync adapters and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Información de depuración</string>
|
||||
<string name="sync_error_permissions">Permisos de DAVdroid</string>
|
||||
<string name="sync_error_permissions_text">Permisos adicionales requeridos</string>
|
||||
<string name="sync_error_calendar">La sincronización de calendario falló (%s)</string>
|
||||
<string name="sync_error_contacts">La sincronización de agenda falló (%s)</string>
|
||||
<string name="sync_error_tasks">La sincronización de tareas falló (%s)</string>
|
||||
<string name="sync_error">Error al %s</string>
|
||||
<string name="sync_error_http_dav">Error de servidor al %s</string>
|
||||
<string name="sync_error_local_storage">Error de base de datos al %s</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>preparando sincronización</item>
|
||||
<item>buscando capacidades</item>
|
||||
<item>procesando entradas borradas localmente</item>
|
||||
<item>preparando entradas creadas/modificadas</item>
|
||||
<item>cargando entradas creadas/modificadas</item>
|
||||
<item>comprobando estado de sincronización</item>
|
||||
<item>enumerando entradas locales</item>
|
||||
<item>enumerando entradas remotas</item>
|
||||
<item>comparando entradas locales/remotas</item>
|
||||
<item>descargando entradas remotas</item>
|
||||
<item>post-procesando</item>
|
||||
<item>guardando estado de sincronización</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">Nombre de usuario/contraseña erróneo</string>
|
||||
<!--cert4android-->
|
||||
<string name="certificate_notification_connection_security">DAVdroid: Seguridad de conexión</string>
|
||||
<string name="trust_certificate_unknown_certificate_found">DAVdroid ha encontrado un certificado desconocido. ¿Quieres que sea válido?</string>
|
||||
</resources>
|
||||
243
app/src/davdroid/res/values-fr/strings.xml
Normal file
243
app/src/davdroid/res/values-fr/strings.xml
Normal file
@@ -0,0 +1,243 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!--common strings-->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="account_title_address_book">Carnet d\'adresses DAVdroid</string>
|
||||
<string name="address_books_authority_title">Carnets d\'adresses</string>
|
||||
<string name="help">Aide</string>
|
||||
<string name="manage_accounts">Gestion des comptes</string>
|
||||
<string name="please_wait">patientez ...</string>
|
||||
<string name="send">Envoyer</string>
|
||||
<!--startup dialogs-->
|
||||
<string name="startup_battery_optimization">Optimisation de la batterie</string>
|
||||
<string name="startup_battery_optimization_message">Android peut désactiver/réduire la synchronisation de DAVdroid après quelques jours. Pour éviter cela, désactivez l\'optimisation de la batterie.</string>
|
||||
<string name="startup_battery_optimization_disable">Désactiver pour DAVdroid</string>
|
||||
<string name="startup_dont_show_again">Ne plus afficher</string>
|
||||
<string name="startup_donate">Open-Source Information</string>
|
||||
<string name="startup_donate_message">Nous sommes heureux que vous utilisez DAVdroid, qui est un logiciel open-source (GPLv3). Parce que développer DAVdroid est un travail difficile et nous a pris de nombreuses heures, s\'il vous plaît envisager de faire un don.</string>
|
||||
<string name="startup_donate_now">Faire un don</string>
|
||||
<string name="startup_donate_later">Plus tard</string>
|
||||
<string name="startup_google_play_accounts_removed">Erreur information Play Store DRM</string>
|
||||
<string name="startup_google_play_accounts_removed_message">Dans certaines conditions, Play Store DRM peut provoquer la disparition de tous les comptes DAVdroid après un redémarrage ou après la mise à niveau de DAVdroid. Si vous êtes concerné par ce problème (et seulement alors), s\'il vous plaît installer \"DAVdroid JB Solution\" du Play Store.</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Plus d\'informations</string>
|
||||
<string name="startup_opentasks_not_installed">L\'application OpenTasks n\'est pas installée</string>
|
||||
<string name="startup_opentasks_not_installed_message">L\'application OpenTasks n\'est pas disponible, donc DAVdroid ne pourra pas synchroniser des listes de tâches.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Après l\'installation OpenTasks, vous devez RE-INSTALLER DAVdroid et ajoutez vos comptes à nouveau (bug Android).</string>
|
||||
<string name="startup_opentasks_not_installed_install">Installer OpenTasks</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Conditions d\'utilisation</string>
|
||||
<string name="about_license_info_no_warranty">Ce programme est fourni sans AUCUNE GARANTIE. C\'est un logiciel libre, et vous êtes en droit de le redistribuer sous certaines conditions.</string>
|
||||
<!--global settings-->
|
||||
<string name="logging_davdroid_file_logging">DAVdroid fichier de journalisation</string>
|
||||
<string name="logging_to_external_storage">Se connecter au stockage externe: %s</string>
|
||||
<string name="logging_couldnt_create_file">Impossible de créer le fichier journal externe: %s</string>
|
||||
<string name="logging_no_external_storage">Stockage externe introuvable</string>
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_open">Ouvrir le tiroir de navigation</string>
|
||||
<string name="navigation_drawer_close">Fermer le tiroir de navigation</string>
|
||||
<string name="navigation_drawer_subtitle">Adaptateur de synchronisation CalDAV/CardDAV</string>
|
||||
<string name="navigation_drawer_about">A propos / Licence</string>
|
||||
<string name="navigation_drawer_settings">Paramètres</string>
|
||||
<string name="navigation_drawer_news_updates">Actualités & mises à jour</string>
|
||||
<string name="navigation_drawer_external_links">Liens externes</string>
|
||||
<string name="navigation_drawer_website">Site Web</string>
|
||||
<string name="navigation_drawer_faq">Foire aux questions</string>
|
||||
<string name="navigation_drawer_forums">Aide/Forum</string>
|
||||
<string name="navigation_drawer_donate">Faire un don</string>
|
||||
<string name="account_list_empty">Bienvenue sur DAVdroid!\n\nVous pouvez maintenant ajouter un compte CalDAV ou CardDAV.</string>
|
||||
<string name="accounts_global_sync_disabled">La synchronisation automatique globale est désactivée</string>
|
||||
<string name="accounts_global_sync_enable">Activer</string>
|
||||
<!--DavService-->
|
||||
<string name="dav_service_refresh_failed">La détection du service a échoué</string>
|
||||
<string name="dav_service_refresh_couldnt_refresh">Impossible d\'actualiser la liste de collection</string>
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">Paramètres</string>
|
||||
<string name="app_settings_user_interface">Interface utilisateur</string>
|
||||
<string name="app_settings_reset_hints">Réinitialiser les astuces</string>
|
||||
<string name="app_settings_reset_hints_summary">Réactiver les astuces qui ont été vues précédemment</string>
|
||||
<string name="app_settings_reset_hints_success">Toutes les astuces seront affichés à nouveau</string>
|
||||
<string name="app_settings_connection">Connexion</string>
|
||||
<string name="app_settings_override_proxy">Ignorer les paramètres proxy</string>
|
||||
<string name="app_settings_override_proxy_on">Utiliser des paramètres proxy personnalisés</string>
|
||||
<string name="app_settings_override_proxy_off">Utiliser les paramètres proxy du système</string>
|
||||
<string name="app_settings_override_proxy_host">Nom de l\'hôte du proxy HTTP</string>
|
||||
<string name="app_settings_override_proxy_port">Port du proxy HTTP</string>
|
||||
<string name="app_settings_security">Sécurité</string>
|
||||
<string name="app_settings_distrust_system_certs">Révoquer les certificats du système</string>
|
||||
<string name="app_settings_distrust_system_certs_on">Les certificats du système et ceux ajoutés par l\'utilisateur ne seront pas dignes de confiance</string>
|
||||
<string name="app_settings_distrust_system_certs_off">Les certificats du système et ceux ajoutés par l\'utilisateur seront dignes de confiance (recommandé)</string>
|
||||
<string name="app_settings_reset_certificates">Réinitialiser les certificats de (non)confiance</string>
|
||||
<string name="app_settings_reset_certificates_summary">Réinitialiser la confiance de tous les certificats personnalisés</string>
|
||||
<string name="app_settings_reset_certificates_success">Tous les certificats personnalisés ont été effacés</string>
|
||||
<string name="app_settings_debug">Débogage</string>
|
||||
<string name="app_settings_log_to_external_storage">Journaliser dans un fichier externe</string>
|
||||
<string name="app_settings_log_to_external_storage_on">Journaliser sur le stockage externe (si disponible)</string>
|
||||
<string name="app_settings_log_to_external_storage_off">Le fichier externe n\'est pas disponible.</string>
|
||||
<string name="app_settings_show_debug_info">Afficher les infos de débogage</string>
|
||||
<string name="app_settings_show_debug_info_details">Voir/partager l\'application et les détails de configuration</string>
|
||||
<!--AccountActivity-->
|
||||
<string name="account_synchronize_now">Synchroniser maintenant</string>
|
||||
<string name="account_synchronizing_now">Synchronisation en cours</string>
|
||||
<string name="account_settings">Paramètres du compte</string>
|
||||
<string name="account_rename">Renommer le compte</string>
|
||||
<string name="account_rename_new_name">Les données locales non enregistrées pourraient être perdues. Une re-synchronisation est nécessaire après avoir renommé le compte. Nouveau nom du compte : </string>
|
||||
<string name="account_rename_rename">Renommer</string>
|
||||
<string name="account_delete">Supprimer le compte</string>
|
||||
<string name="account_delete_confirmation_title">Voulez-vous vraiment supprimer le compte?</string>
|
||||
<string name="account_delete_confirmation_text">Toutes les copies locales des carnets d\'adresses, des calendriers et des listes de tâches seront supprimées.</string>
|
||||
<string name="account_carddav">CardDAV (les carnets d\'adresse) </string>
|
||||
<string name="account_caldav">CalDAV (les agendas) </string>
|
||||
<string name="account_webcal">WebCal (les anciens agenda)</string>
|
||||
<string name="account_refresh_address_book_list">Actualiser le carnet d\'adresses</string>
|
||||
<string name="account_create_new_address_book">Créer un nouveau carnet d\'adresses</string>
|
||||
<string name="account_refresh_calendar_list">Actualiser le calendrier</string>
|
||||
<string name="account_create_new_calendar">Créer un nouveau calendrier</string>
|
||||
<string name="account_no_webcal_handler_found">Aucune application compatible WebCal</string>
|
||||
<string name="account_install_icsdroid">Installer ICSdroid</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">Autorisations DAVdroid</string>
|
||||
<string name="permissions_calendar">Autorisations calendrier</string>
|
||||
<string name="permissions_calendar_details">Pour synchroniser les événements CalDAV avec vos calendriers locaux, DAVdroid a besoin d\'accéder à vos calendriers.</string>
|
||||
<string name="permissions_calendar_request">Demande d\'autorisations d\'accéder au calendrier</string>
|
||||
<string name="permissions_contacts">Autorisations d\'accès aux contacts</string>
|
||||
<string name="permissions_contacts_details">Pour synchroniser les carnets d\'adresses de CardDAV avec votre carnet d\'adresses local, DAVdroid a besoin d\'accéder à vos contacts.</string>
|
||||
<string name="permissions_contacts_request">Demande d\'autorisations d\'accéder aux contacts</string>
|
||||
<string name="permissions_opentasks">Autorisations OpenTasks</string>
|
||||
<string name="permissions_opentasks_details">Pour synchroniser les tâches de CalDAV avec vos listes de tâches locales, DAVdroid a besoin d\'accéder à OpenTasks.</string>
|
||||
<string name="permissions_opentasks_request">Demande d\'autorisations d\'accéder à OpenTasks</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_help_url">https://davdroid.bitfire.at/configuration/?pk_campaign=davdroid-app</string>
|
||||
<string name="login_title">Ajouter un compte</string>
|
||||
<string name="login_type_email">Connexion avec une adresse email</string>
|
||||
<string name="login_email_address">Adresse mail</string>
|
||||
<string name="login_email_address_error">Une adresse e-mail valide est requise</string>
|
||||
<string name="login_password">Mot de passe</string>
|
||||
<string name="login_password_required">Mot de passe requis</string>
|
||||
<string name="login_type_url">Connexion avec une URL et un nom d\'utilisateur</string>
|
||||
<string name="login_url_must_be_http_or_https">L\'URL doit commencer par http(s)://</string>
|
||||
<string name="login_url_host_name_required">Nom d\'hôte requis</string>
|
||||
<string name="login_user_name">Nom d\'utilisateur</string>
|
||||
<string name="login_user_name_required">Nom d\'utilisateur requis</string>
|
||||
<string name="login_base_url">URL de base</string>
|
||||
<string name="login_login">Se connecter</string>
|
||||
<string name="login_back">Retour</string>
|
||||
<string name="login_create_account">Créer un compte</string>
|
||||
<string name="login_account_name">Nom du compte</string>
|
||||
<string name="login_account_name_info">Utilisez votre adresse e-mail comme nom de compte car Android utilisera ce nom en tant que champ ORGANISATEUR pour les événements que vous créerez. Vous ne pouvez pas avoir deux comptes avec le même nom.</string>
|
||||
<string name="login_account_contact_group_method">Méthode pour les contacts de type groupe :</string>
|
||||
<string name="login_account_name_required">Nom du compte requis</string>
|
||||
<string name="login_account_not_created">Le compte n\'a pas pu être créé</string>
|
||||
<string name="login_configuration_detection">Détection de la configuration</string>
|
||||
<string name="login_querying_server">Veuillez patienter, nous interrogeons le serveur ...</string>
|
||||
<string name="login_no_caldav_carddav">Aucun accès possible au service CalDAV ou CardDAV.</string>
|
||||
<string name="login_view_logs">Voir infos de débogage</string>
|
||||
<!--AccountSettingsActivity-->
|
||||
<string name="settings_title">Paramètres: %s</string>
|
||||
<string name="settings_authentication">Authentification</string>
|
||||
<string name="settings_username">Nom d\'utilisateur</string>
|
||||
<string name="settings_enter_username">Saisissez votre nom d\'utilisateur :</string>
|
||||
<string name="settings_password">Mot de passe</string>
|
||||
<string name="settings_password_summary">Mettre à jour le mot de passe </string>
|
||||
<string name="settings_enter_password">Saisissez votre mot de passe :</string>
|
||||
<string name="settings_sync">Synchronisation</string>
|
||||
<string name="settings_sync_interval_contacts">Intervalle de synchronisation des carnets d\'adresses</string>
|
||||
<string name="settings_sync_summary_manually">Manuellement</string>
|
||||
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Toutes les %d minutes et immédiatement après un changement local</string>
|
||||
<string name="settings_sync_interval_calendars">Intervalle de synchronisation des agendas</string>
|
||||
<string name="settings_sync_interval_tasks">Intervalle de synchronisation des tâches</string>
|
||||
<string-array name="settings_sync_interval_names">
|
||||
<item>Manuellement</item>
|
||||
<item>Tous les quarts d\'heure</item>
|
||||
<item>Toutes les demi-heures</item>
|
||||
<item>Toutes les heures</item>
|
||||
<item>Toutes les deux heures</item>
|
||||
<item>Toutes les quatre heures</item>
|
||||
<item>Une fois par jour</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">Synchronisation en Wifi seulement</string>
|
||||
<string name="settings_sync_wifi_only_on">La synchronisation est limitée aux connexions WiFi</string>
|
||||
<string name="settings_sync_wifi_only_off">Le type de connexion n\'est pas pris en charge</string>
|
||||
<string name="settings_sync_wifi_only_ssids">Restrictions concernant le nom de réseau WiFi (SSID)</string>
|
||||
<string name="settings_sync_wifi_only_ssids_on">Synchronisation possible seulement en %s</string>
|
||||
<string name="settings_sync_wifi_only_ssids_off">Toutes les connexions WiFi seront utilisées</string>
|
||||
<string name="settings_sync_wifi_only_ssids_message">Liste des points d\'accès WiFi (SSID) autorisés, séparés par des virgules. (Laissez vide pour tous)</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">Méthode pour les contacts de type groupe</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string-array name="settings_contact_group_method_entries">
|
||||
<item>Les groupes sont des VCards indépendantes</item>
|
||||
<item>Les groupes sont des catégories pour chacun des contacts</item>
|
||||
</string-array>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Limite des événements passés</string>
|
||||
<string name="settings_sync_time_range_past_none">Tous les événements seront synchronisés</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="one">Les événements de plus d’un jour passé seront ignorés</item>
|
||||
<item quantity="other">Les événements de plus de %d jours passés seront ignorés</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Les événements antérieurs à ce nombre de jours seront ignorés (peut être 0). Laissez vide pour synchroniser tous les événements.</string>
|
||||
<string name="settings_manage_calendar_colors">Choisir couleur du calendrier</string>
|
||||
<string name="settings_manage_calendar_colors_on">Les couleurs de calendrier sont gérées par DAVdroid</string>
|
||||
<string name="settings_manage_calendar_colors_off">Les couleurs de calendrier ne sont pas gérées par DAVdroid</string>
|
||||
<string name="settings_event_colors">Couleur associée aux événements</string>
|
||||
<string name="settings_event_colors_on">Synchroniser la couleur associée aux événements</string>
|
||||
<string name="settings_event_colors_off">Ne pas synchroniser la couleur associée aux événements</string>
|
||||
<string name="settings_event_colors_off_confirm">Modifier la couleur associée aux événements peut affecter les valeurs déjà synchronisées.</string>
|
||||
<!--collection management-->
|
||||
<string name="create_addressbook">Créer un carnet d\'adresses</string>
|
||||
<string name="create_addressbook_display_name_hint">Mon carnet d\'adresses</string>
|
||||
<string name="create_calendar">Créer une collection CalDAV</string>
|
||||
<string name="create_calendar_display_name_hint">Mon calendrier</string>
|
||||
<string name="create_calendar_time_zone">Fuseau horaire:</string>
|
||||
<string name="create_calendar_type">Type de collection:</string>
|
||||
<string name="create_calendar_type_only_events">Calendrier (événements seulement)</string>
|
||||
<string name="create_calendar_type_only_tasks">Liste de tâches (tâches seulement)</string>
|
||||
<string name="create_calendar_type_events_and_tasks">Fusionner (événements et tâches)</string>
|
||||
<string name="create_collection_color">Choisir une couleur pour la collection</string>
|
||||
<string name="create_collection_creating">Création collection</string>
|
||||
<string name="create_collection_display_name">Le nom affiché (titre) pour cette collection:</string>
|
||||
<string name="create_collection_display_name_required">Titre requis</string>
|
||||
<string name="create_collection_description">Description (facultatif)</string>
|
||||
<string name="create_collection_home_set">Accueil:</string>
|
||||
<string name="create_collection_create">Créer</string>
|
||||
<string name="delete_collection">Supprimer la collection</string>
|
||||
<string name="delete_collection_confirm_title">Êtes-vous sur?</string>
|
||||
<string name="delete_collection_confirm_warning">Cette collection (%s) et toutes ses données seront supprimées du serveur.</string>
|
||||
<string name="delete_collection_deleting_collection">Suppression de la collection</string>
|
||||
<!--ExceptionInfoFragment-->
|
||||
<string name="exception">Une erreur est survenue.</string>
|
||||
<string name="exception_httpexception">Une erreur HTTP est survenue.</string>
|
||||
<string name="exception_ioexception">Une erreur I/O est survenue.</string>
|
||||
<string name="exception_show_details">Voir détails</string>
|
||||
<!--sync adapters and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Infos de débogage</string>
|
||||
<string name="sync_error_permissions">Autorisations DAVdroid</string>
|
||||
<string name="sync_error_permissions_text">Autorisations supplémentaires demandées</string>
|
||||
<string name="sync_error_calendar">Échec de la synchronisation du calendrier (%s)</string>
|
||||
<string name="sync_error_contacts">Échec de la synchronisation du carnet d\'adresse (%s)</string>
|
||||
<string name="sync_error_tasks">Échec de la synchronisation (%s)</string>
|
||||
<string name="sync_error">Erreur durant %s</string>
|
||||
<string name="sync_error_http_dav">Erreur de serveur durant %s</string>
|
||||
<string name="sync_error_local_storage">Erreur de base de donnée durant %s</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>prépare la synchronisation</item>
|
||||
<item>demande les autorisations</item>
|
||||
<item>procède à la suppression des entrées locales</item>
|
||||
<item>prépare les entrées créées/modifiées</item>
|
||||
<item>envoi les entrées créées/modifiées</item>
|
||||
<item>vérifie l\'état de la synchronisation</item>
|
||||
<item>liste les entrées locales</item>
|
||||
<item>liste les entrées distantes</item>
|
||||
<item>compare les entrées locales/distantes</item>
|
||||
<item>télécharge les entrées distantes</item>
|
||||
<item>post-traitement</item>
|
||||
<item>enregistre l\'état de la synchronisation</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">Nom d\'utilisateur ou mot de passe erroné</string>
|
||||
<!--cert4android-->
|
||||
<string name="certificate_notification_connection_security">DAVdroid : Sécurité de la connexion</string>
|
||||
<string name="trust_certificate_unknown_certificate_found">DAVdroid a rencontré un certificat inconnu. Voulez-vous lui faire confiance?</string>
|
||||
</resources>
|
||||
245
app/src/davdroid/res/values-hu/strings.xml
Normal file
245
app/src/davdroid/res/values-hu/strings.xml
Normal file
@@ -0,0 +1,245 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!--common strings-->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="account_title_address_book">DAVdroid címjegyzék</string>
|
||||
<string name="address_books_authority_title">Címjegyzékek</string>
|
||||
<string name="help">Súgó</string>
|
||||
<string name="manage_accounts">Fiókok kezelése</string>
|
||||
<string name="please_wait">Kérjük, várjon ...</string>
|
||||
<string name="send">Küldés</string>
|
||||
<string name="homepage_url">https://davdroid.bitfire.at/?pk_campaign=davdroid-app</string>
|
||||
<!--startup dialogs-->
|
||||
<string name="startup_battery_optimization">Akkumulátoroptimalizálás </string>
|
||||
<string name="startup_battery_optimization_message">Az operációs rendszer a DAVdroid szinkronizálást pár nap után leállíthatja vagy visszafoghatja. Ennek elkerülésére kapcsolja ki az akkumulátoroptimalizálást.</string>
|
||||
<string name="startup_battery_optimization_disable">Kikapcsolás a DAVdroid kapcsán</string>
|
||||
<string name="startup_dont_show_again">Ne jelenjen meg többet</string>
|
||||
<string name="startup_donate">A forrás nyíltságával kapcsolatos információk</string>
|
||||
<string name="startup_donate_message">Örülünk, hogy használja a DAVdroidot. A DAVdroid nyílt forráskódú (GPLv3) szoftver, de a fejlesztése kemény munkát jelent, már eddig több ezer munkaórát emésztett fel, ezért kérjük, fontolja meg, hogy támogassa munkánkat.</string>
|
||||
<string name="startup_donate_now">Támogatás küldése</string>
|
||||
<string name="startup_donate_later">Talán később</string>
|
||||
<string name="startup_google_play_accounts_removed">Play Áruház DRM hibával kapcsolatos információ</string>
|
||||
<string name="startup_google_play_accounts_removed_message">Bizonyos körülmények között a Play Áruház DRM okozhatja azt, hogy az eszköz újraindítását vagy a DAVdroid frissítését követően a DAVdroid fiókok eltűnnek. Amennyiben (és csak amennyiben) érinti Önt ez a probléma, telepítse a \"DAVdroid JB Workaround\" alkalmazást Play Áruházból.</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">További információk</string>
|
||||
<string name="startup_opentasks_not_installed">Az OpenTasks nincs telepítve</string>
|
||||
<string name="startup_opentasks_not_installed_message">Az OpenTasks alkalmazás nincs telepítve, így a DAVdroid nem lesz képes szinkronizálni feladatlistákat.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Az OpenTasks telepítését követően újra kell telepíteni a DAVdroit alkalmazást és újra fel kell venni a fiókokat (Android hiba).</string>
|
||||
<string name="startup_opentasks_not_installed_install">Az OpenTasks telepítése</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Licencfeltételek</string>
|
||||
<string name="about_license_info_no_warranty">Ehhez a program SEMMIFÉLE GARANCIA NEM JÁR. Ez a program szabad szoftver, ami a bizonyos feltételek mellett szabadon terjeszthető.</string>
|
||||
<!--global settings-->
|
||||
<string name="logging_davdroid_file_logging">DAVdroid fájlalapú naplózás</string>
|
||||
<string name="logging_to_external_storage">Naplózás külső tárhelyre: %s</string>
|
||||
<string name="logging_couldnt_create_file">Külső naplófájl létrehozása sikertelen: %s</string>
|
||||
<string name="logging_no_external_storage">A külső tárhely nem érhető el</string>
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_open">Navigációs fiók megnyitása</string>
|
||||
<string name="navigation_drawer_close">Navigációs fiók lezárása</string>
|
||||
<string name="navigation_drawer_subtitle">CalDAV/CardDAV szinkronizációs adapter</string>
|
||||
<string name="navigation_drawer_about">Névjegy / Licenc</string>
|
||||
<string name="navigation_drawer_settings">Beállítások</string>
|
||||
<string name="navigation_drawer_news_updates">Hírek és frissítések</string>
|
||||
<string name="navigation_drawer_external_links">Weblapok</string>
|
||||
<string name="navigation_drawer_website">Honlap</string>
|
||||
<string name="navigation_drawer_faq">GYIK</string>
|
||||
<string name="navigation_drawer_faq_url">https://davdroid.bitfire.at/faq/?pk_campaign=davdroid-app</string>
|
||||
<string name="navigation_drawer_forums">Segítség / Fórumok</string>
|
||||
<string name="navigation_drawer_donate">Támogatás</string>
|
||||
<string name="account_list_empty">Üdvözöljük a DAVdroid felhasználók között!\n\nMost már felvehet CalDAV/CardDav fiókokat.</string>
|
||||
<string name="accounts_global_sync_disabled">A rendszerszintű automatikus szinkronizálás ki van kapcsolva</string>
|
||||
<string name="accounts_global_sync_enable">Bekapcsolás</string>
|
||||
<!--DavService-->
|
||||
<string name="dav_service_refresh_failed">Szolgáltatások felderítése nem sikerült</string>
|
||||
<string name="dav_service_refresh_couldnt_refresh">Gyűjteménylista frissítése nem sikerült</string>
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">Beállítások</string>
|
||||
<string name="app_settings_user_interface">Felhasználói felület</string>
|
||||
<string name="app_settings_reset_hints">Tippek visszaállítása</string>
|
||||
<string name="app_settings_reset_hints_summary">Újra jelenjen meg az összes tipp</string>
|
||||
<string name="app_settings_reset_hints_success">Az összes tipp újra meg fog jelenni</string>
|
||||
<string name="app_settings_connection">Kapcsolat</string>
|
||||
<string name="app_settings_override_proxy">Proxybeállítások felülírása</string>
|
||||
<string name="app_settings_override_proxy_on">Egyedi proxybeállítások</string>
|
||||
<string name="app_settings_override_proxy_off">Az alapértelmezett proxybeállítás használata</string>
|
||||
<string name="app_settings_override_proxy_host">HTTP proxyállomás neve</string>
|
||||
<string name="app_settings_override_proxy_port">HTTP proxy port</string>
|
||||
<string name="app_settings_security">Biztonság</string>
|
||||
<string name="app_settings_distrust_system_certs">A rendszertanúsítványok elfogadása</string>
|
||||
<string name="app_settings_distrust_system_certs_on">A rendszer által kezelt, előre vagy felhasználó által telepített tanúsítványok figyelmen kívül lesznek hagyva</string>
|
||||
<string name="app_settings_distrust_system_certs_off">A rendszer által kezelt, előre vagy felhasználó által telepített tanúsítványok megbízhatóak (javasolt)</string>
|
||||
<string name="app_settings_reset_certificates">A tanúsítványok megbízhatóságának törlésére</string>
|
||||
<string name="app_settings_reset_certificates_summary">A tanúsítványok megbízhatóságával kapcsolatos beállítások törlésére</string>
|
||||
<string name="app_settings_reset_certificates_success">A tanúsítványok megbízhatóságával kapcsolatos beállítások törölve</string>
|
||||
<string name="app_settings_debug">Hibakeresés</string>
|
||||
<string name="app_settings_log_to_external_storage">Naplózás fájlba</string>
|
||||
<string name="app_settings_log_to_external_storage_on">Naplózás külső tárhelyre (ha elérhető)</string>
|
||||
<string name="app_settings_log_to_external_storage_off">Fájlalapú naplózás kikapcsolva</string>
|
||||
<string name="app_settings_show_debug_info">Hibakeresési információ megtekintése</string>
|
||||
<string name="app_settings_show_debug_info_details">Szoftver- és konfigurációs részletek megtekintése/megosztása</string>
|
||||
<!--AccountActivity-->
|
||||
<string name="account_synchronize_now">Szinkronizálás most</string>
|
||||
<string name="account_synchronizing_now">Szinkronizálás</string>
|
||||
<string name="account_settings">Fiókbeállítások</string>
|
||||
<string name="account_rename">Fiók átnevezése</string>
|
||||
<string name="account_rename_new_name">Az elmentetlen helyben tárolt adatok elvesznek. Az átnevezés után szinkronizálásra lesz szükség. Új fióknév:</string>
|
||||
<string name="account_rename_rename">Átnevez</string>
|
||||
<string name="account_delete">Fiók törlése</string>
|
||||
<string name="account_delete_confirmation_title">Valóban törölni akarja a fiókot?</string>
|
||||
<string name="account_delete_confirmation_text">Az összes címjegyzék, naptár és feladatlista helyi példányai törölve lesznek.</string>
|
||||
<string name="account_carddav">CardDAV</string>
|
||||
<string name="account_caldav">CalDAV</string>
|
||||
<string name="account_webcal">Webcal</string>
|
||||
<string name="account_refresh_address_book_list">Címjegyzék-lista frissítése</string>
|
||||
<string name="account_create_new_address_book">Új címjegyzék létrehozása</string>
|
||||
<string name="account_refresh_calendar_list">Naptárlista frissítése</string>
|
||||
<string name="account_create_new_calendar">Új naptár létrehozása</string>
|
||||
<string name="account_no_webcal_handler_found">Nem található Webcal-képes alkalmazás</string>
|
||||
<string name="account_install_icsdroid">ICSdroid telepítése</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">DAVdroid engedélyek </string>
|
||||
<string name="permissions_calendar">Naptárengedély</string>
|
||||
<string name="permissions_calendar_details">A CalDAV naptárak és a helyi naptárak szinkronizálásához a DAVdroid naptárhozzáférést igényel.</string>
|
||||
<string name="permissions_calendar_request">Naptárhozzáférés igénylése</string>
|
||||
<string name="permissions_contacts">Névjegyengedélyek</string>
|
||||
<string name="permissions_contacts_details">A CardDAV címlisták és a helyi címlisták szinkronizálásához a névjegyhozzáférést igényel.</string>
|
||||
<string name="permissions_contacts_request">Névjegyengedélyek igénylése</string>
|
||||
<string name="permissions_opentasks">OpenTasks engedélyek</string>
|
||||
<string name="permissions_opentasks_details">A CalDAV feladatlisták és a helyi feladatlisták szinkronizálásához a DAVdroid OpenTasks hozzáférést igényel.</string>
|
||||
<string name="permissions_opentasks_request">OpenTasks engedélyek igénylése</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_help_url">https://davdroid.bitfire.at/configuration/?pk_campaign=davdroid-app</string>
|
||||
<string name="login_title">Fiók hozzáadása</string>
|
||||
<string name="login_type_email">Bejelentkezés email cím segítségével</string>
|
||||
<string name="login_email_address">Email cím:</string>
|
||||
<string name="login_email_address_error">Érvényes email cím megadása feltétlenül szükséges</string>
|
||||
<string name="login_password">Jelszó</string>
|
||||
<string name="login_password_required">A jelszó megadása szükséges</string>
|
||||
<string name="login_type_url">Bejelentkezés URL és felhasználónév segítségével</string>
|
||||
<string name="login_url_must_be_http_or_https">Az URL elején szerepeljen http(s)://</string>
|
||||
<string name="login_url_host_name_required">A szervernév megadása feltétlenül szükséges</string>
|
||||
<string name="login_user_name">Felhasználónév</string>
|
||||
<string name="login_user_name_required">A felhasználónév megadása feltétlenül szükséges</string>
|
||||
<string name="login_base_url">URL-törzs</string>
|
||||
<string name="login_login">Bejelentkezés</string>
|
||||
<string name="login_back">Vissza</string>
|
||||
<string name="login_create_account">Fiók létrehozása</string>
|
||||
<string name="login_account_name">A fiók neve</string>
|
||||
<string name="login_account_name_info">Használja az email címet fióknévként, mert később a létrehozandó események szervezőjeként (ORGANIZER mező) az Android ezt fogja használni. Két fiókot nem lehet azonos néven létrehozni.</string>
|
||||
<string name="login_account_contact_group_method">A csoportok kezelésének módja:</string>
|
||||
<string name="login_account_name_required">A fióknév megadása feltétlenül szükséges</string>
|
||||
<string name="login_account_not_created">A fiók létrehozása nem sikerült</string>
|
||||
<string name="login_configuration_detection">A konfiguráció felderítése</string>
|
||||
<string name="login_querying_server">Kérjük, várjon, a szerver lekérdezése...</string>
|
||||
<string name="login_no_caldav_carddav">Nem található CalDAV vagy CardDAV szolgáltatás.</string>
|
||||
<string name="login_view_logs">Naplóbejegyzések megtekintése</string>
|
||||
<!--AccountSettingsActivity-->
|
||||
<string name="settings_title">Beállítások: %s</string>
|
||||
<string name="settings_authentication">Authentikáció</string>
|
||||
<string name="settings_username">Felhasználónév</string>
|
||||
<string name="settings_enter_username">Adja meg a felhasználónevet:</string>
|
||||
<string name="settings_password">Jelszó</string>
|
||||
<string name="settings_password_summary">Adja meg a szerveren érvényes új jelszót.</string>
|
||||
<string name="settings_enter_password">Adja meg a jelszót:</string>
|
||||
<string name="settings_sync">Szinkronizálás</string>
|
||||
<string name="settings_sync_interval_contacts">Névjegyszinkronizálás sűrűsége</string>
|
||||
<string name="settings_sync_summary_manually">Manuális</string>
|
||||
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Minden %d percben + az eszközön történt módosítás után</string>
|
||||
<string name="settings_sync_interval_calendars">Naptárszinkronizálás sűrűsége</string>
|
||||
<string name="settings_sync_interval_tasks">Feladatlisták szinkronizálásának sűrűsége</string>
|
||||
<string-array name="settings_sync_interval_names">
|
||||
<item>Manális</item>
|
||||
<item>15 percenként</item>
|
||||
<item>30 percenként</item>
|
||||
<item>Óránként</item>
|
||||
<item>Kétóránként</item>
|
||||
<item>Négyóránként</item>
|
||||
<item>Naponta</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">Szinkronizálás csak WIFI-n</string>
|
||||
<string name="settings_sync_wifi_only_on">Csak WIFI kapcsolat keresztül történjen szinkronizálás</string>
|
||||
<string name="settings_sync_wifi_only_off">Szinkronizálás a kapcsolat típusától függetlenül</string>
|
||||
<string name="settings_sync_wifi_only_ssids">WiFi SSID szűrés</string>
|
||||
<string name="settings_sync_wifi_only_ssids_on">Az alábbi hálózatok használhatók: %s</string>
|
||||
<string name="settings_sync_wifi_only_ssids_off">Minden hálózat használható</string>
|
||||
<string name="settings_sync_wifi_only_ssids_message">A használható WiFi hálózatok nevei (SSID), vesszővel elválasztva (hagyja üresen, ha nem akar szűrést beállítani)</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">A csoportok kezelésének módja</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string-array name="settings_contact_group_method_entries">
|
||||
<item>Minden csoport egy különálló VCard</item>
|
||||
<item>A csoportok a kapcsolatonkéni kategóriák</item>
|
||||
</string-array>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Múltbéli események időkorlátja</string>
|
||||
<string name="settings_sync_time_range_past_none">Minden esemény szinkronizálása</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="one">Az egy napnál régebbi események figyelmen kívül hagyása</item>
|
||||
<item quantity="other">A %d napnál régebbi események figyelmen kívül hagyása</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Az ennyi napnál (lehet 0) régebbi események figyelmen kívül lesznek hagyva. Hagyja üresen, ha minden múltbéli eseményt szinkronizálni akar.</string>
|
||||
<string name="settings_manage_calendar_colors">Naptárszínek kezelése</string>
|
||||
<string name="settings_manage_calendar_colors_on">A naptárszíneket a DAVdroid kezeli</string>
|
||||
<string name="settings_manage_calendar_colors_off">A naptárszíneket nem a DAVdroid kezeli</string>
|
||||
<string name="settings_event_colors">Eseményszínek támogatása</string>
|
||||
<string name="settings_event_colors_on">Eseményszínek szinkronlzálása</string>
|
||||
<string name="settings_event_colors_off">Az eseményszínek szinkronizálásának elhagyása</string>
|
||||
<string name="settings_event_colors_off_confirm">A szinkronizálás kikapcsolása a már szinkronizált eseményszíneket törölheti.</string>
|
||||
<!--collection management-->
|
||||
<string name="create_addressbook">Címjegyzék létrehozása</string>
|
||||
<string name="create_addressbook_display_name_hint">Új címjegyzék</string>
|
||||
<string name="create_calendar">CalDAV gyűjtemény létrehozása</string>
|
||||
<string name="create_calendar_display_name_hint">Új naptár</string>
|
||||
<string name="create_calendar_time_zone">Időzóna:</string>
|
||||
<string name="create_calendar_type">Gyűjtemény típusa:</string>
|
||||
<string name="create_calendar_type_only_events">Naptár (csak események)</string>
|
||||
<string name="create_calendar_type_only_tasks">Feladatlista (csak feladatok)</string>
|
||||
<string name="create_calendar_type_events_and_tasks">Kombinált (események és feladatok)</string>
|
||||
<string name="create_collection_color">A gyűjtemény színének kiválasztása</string>
|
||||
<string name="create_collection_creating">Gyűjtemény létrehozása</string>
|
||||
<string name="create_collection_display_name">A gyűjtemény megnevezése (címe)</string>
|
||||
<string name="create_collection_display_name_required">A cím megadása feltétlenül szükséges</string>
|
||||
<string name="create_collection_description">Leírás (opcionális):</string>
|
||||
<string name="create_collection_home_set">Gyűjtemények tárolója:</string>
|
||||
<string name="create_collection_create">Létrehozás</string>
|
||||
<string name="delete_collection">Gyűjtemény törlése</string>
|
||||
<string name="delete_collection_confirm_title">Biztos?</string>
|
||||
<string name="delete_collection_confirm_warning">A gyűjtemény (%s) és a hozzá tartozó adatok törölve lesznek a szerverről.</string>
|
||||
<string name="delete_collection_deleting_collection">Gyűjtemény törlése</string>
|
||||
<!--ExceptionInfoFragment-->
|
||||
<string name="exception">Hiba történt.</string>
|
||||
<string name="exception_httpexception">HTTP hiba történt.</string>
|
||||
<string name="exception_ioexception">I/O hiba történt.</string>
|
||||
<string name="exception_show_details">Részletek megjelenítése</string>
|
||||
<!--sync adapters and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Hibakeresési információ</string>
|
||||
<string name="sync_error_permissions">DAVdroid engedélyek </string>
|
||||
<string name="sync_error_permissions_text">További engedélyek szükségesek</string>
|
||||
<string name="sync_error_calendar">A naptár szinkronizálása nem sikerült (%s)</string>
|
||||
<string name="sync_error_contacts">A címjegyzék szinkronizálása nem sikerült (%s)</string>
|
||||
<string name="sync_error_tasks">A feladatok szinkronizálása nem sikerült (%s)</string>
|
||||
<string name="sync_error">Hiba az alábbi művelet közben: %s</string>
|
||||
<string name="sync_error_http_dav">Szerver oldali hiba az alábbi művelet közben: %s</string>
|
||||
<string name="sync_error_local_storage">Adatbázishiba az alábbi művelet közben: %s</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>szinkronizáció előkészítése </item>
|
||||
<item>szerver képességeinek lekérdezése</item>
|
||||
<item>a helyben törölt bejegyzések feldolgozása</item>
|
||||
<item>az új vagy módosított bejegyzések gyűjtése </item>
|
||||
<item>az új vagy módosított bejegyzések feltöltése</item>
|
||||
<item>szinkronizációs állapot ellenőrzése</item>
|
||||
<item>helyi bejegyzések listázása</item>
|
||||
<item>távoli bejegyzések listázása</item>
|
||||
<item>helyi és távoli bejegyzések összehasonlítása</item>
|
||||
<item>távoli bejegyzések letöltése</item>
|
||||
<item>utófeldolgozás</item>
|
||||
<item>szinkronizációs állapot mentése</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">A felhasználónév vagy jelszó hibás</string>
|
||||
<!--cert4android-->
|
||||
<string name="certificate_notification_connection_security">DAVdroid: kapcsolatbiztonság</string>
|
||||
<string name="trust_certificate_unknown_certificate_found">Egy eddig ismeretlen tanúsítvány érkezett. Megbízhatónak kívánja elfogadni?</string>
|
||||
</resources>
|
||||
234
app/src/davdroid/res/values-it/strings.xml
Normal file
234
app/src/davdroid/res/values-it/strings.xml
Normal file
@@ -0,0 +1,234 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!--common strings-->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="account_title_address_book">Rubrica degli indirizzi DAVdroid</string>
|
||||
<string name="address_books_authority_title">Rubriche degli indirizzi</string>
|
||||
<string name="help">Aiuto</string>
|
||||
<string name="manage_accounts">Gestione account</string>
|
||||
<string name="please_wait">Attendere prego …</string>
|
||||
<string name="send">Invia</string>
|
||||
<!--startup dialogs-->
|
||||
<string name="startup_battery_optimization">Ottimizazione della batteria</string>
|
||||
<string name="startup_battery_optimization_message">Android può ridurre o disabilitare la sincronizzazione di DAVdroid dopo alcuni giorni. Per prevenire questo comportamento disabilita l\'ottimizzazione della batteria</string>
|
||||
<string name="startup_battery_optimization_disable">Disabilita per DAVdroid</string>
|
||||
<string name="startup_dont_show_again">Non mostrare più</string>
|
||||
<string name="startup_donate">Informazioni sull\'Open-Source</string>
|
||||
<string name="startup_donate_message">Siamo soddisfatti del tuo uso di DAVdroid, programma Open-Source (GPLv3). Poiché lo sviluppo è un\'iniziativa complessa che comporta molte ore di lavoro ti invitiamo a fare una donazione.</string>
|
||||
<string name="startup_donate_now">Mostra pagina donazioni</string>
|
||||
<string name="startup_donate_later">Più tardi</string>
|
||||
<string name="startup_google_play_accounts_removed">Informazioni sul bug del DRM di Play Store</string>
|
||||
<string name="startup_google_play_accounts_removed_message">In alcune condizioni il DRM di Play Store può causare la perdita di tutti gli account DAVdroid dopo un riavvio o dopo un aggiornamento di DAVdroid. Se verificate questo problema installate successivamente \"DAVdroid JB Workaround\" da Play Store.</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Maggiori informazioni</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks non installata</string>
|
||||
<string name="startup_opentasks_not_installed_message">L\'applicazione OpenTasks non è installata: di conseguenza DAVdroid non potrà sincronizzare l\'elenco delle attività.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Dopo l\'installazione di OpenTasks è necessario INSTALLARE NUOVAMENTE DAVdroid e aggiungere ancora gli account (per un bug di Android).</string>
|
||||
<string name="startup_opentasks_not_installed_install">Installa OpenTasks</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Termini di licenza</string>
|
||||
<string name="about_license_info_no_warranty">Il programma è distribuito SENZA ALCUNA GARANZIA. È software libero e può essere redistribuito sotto alcune condizioni.</string>
|
||||
<!--global settings-->
|
||||
<string name="logging_davdroid_file_logging">Invio del log di DAVdroid su file</string>
|
||||
<string name="logging_to_external_storage">Log su dispositivo esterno: %s</string>
|
||||
<string name="logging_couldnt_create_file">Non riesco a creare il file di log esterno: %s</string>
|
||||
<string name="logging_no_external_storage">Dispositivo esterno non disponibile</string>
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_open">Apri barra di navigazione</string>
|
||||
<string name="navigation_drawer_close">Chiudi barra di navigazione</string>
|
||||
<string name="navigation_drawer_subtitle">CalDAV/CardDAV adattatore di sincronizzazione</string>
|
||||
<string name="navigation_drawer_about">Informazioni / Licenza</string>
|
||||
<string name="navigation_drawer_settings">Impostazioni</string>
|
||||
<string name="navigation_drawer_news_updates">Notizie & aggiornamenti</string>
|
||||
<string name="navigation_drawer_external_links">Link esterni</string>
|
||||
<string name="navigation_drawer_website">Sito web</string>
|
||||
<string name="navigation_drawer_faq">Domande Frequenti</string>
|
||||
<string name="navigation_drawer_donate">Donazione</string>
|
||||
<string name="account_list_empty">Benvenuto a DAVdroid!\n\nÈ ora possibile aggiungere account CalDAV/CardDAV.</string>
|
||||
<string name="accounts_global_sync_disabled">La sincronizzazione automatica dell\'intero sistema è disabilitata</string>
|
||||
<string name="accounts_global_sync_enable">Attiva</string>
|
||||
<!--DavService-->
|
||||
<string name="dav_service_refresh_failed">Fallita l\'individuazione dei servizi</string>
|
||||
<string name="dav_service_refresh_couldnt_refresh">Impossibile aggiornare la lista delle raccolte</string>
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">Impostazioni</string>
|
||||
<string name="app_settings_user_interface">Interfaccia utente</string>
|
||||
<string name="app_settings_reset_hints">Reimposta i suggerimenti</string>
|
||||
<string name="app_settings_reset_hints_summary">Riabilita i suggerimenti precedentemente disabilitati</string>
|
||||
<string name="app_settings_reset_hints_success">I suggerimenti verranno mostrati</string>
|
||||
<string name="app_settings_connection">Connessione</string>
|
||||
<string name="app_settings_override_proxy">Non rispettare la impostazioni del proxy</string>
|
||||
<string name="app_settings_override_proxy_on">Impostazioni personalizzate del proxy</string>
|
||||
<string name="app_settings_override_proxy_off">Usa le impostazioni di sistema del proxy</string>
|
||||
<string name="app_settings_override_proxy_host">Nome host del proxy HTTP</string>
|
||||
<string name="app_settings_override_proxy_port">Porta del proxy HTTP</string>
|
||||
<string name="app_settings_security">Sicurezza</string>
|
||||
<string name="app_settings_distrust_system_certs">Non ti fidare dei certificati di sistema</string>
|
||||
<string name="app_settings_distrust_system_certs_on">Le CA di sistema e quelle aggiunte dall\'utente non sono affidabili</string>
|
||||
<string name="app_settings_distrust_system_certs_off">Le CA di sistema e quelle aggiunte dall\'utente sono affidabili (raccomandato)</string>
|
||||
<string name="app_settings_reset_certificates">Reimposta la fiducia in tutti i certificati</string>
|
||||
<string name="app_settings_reset_certificates_summary">Reimposta la fiducia nei certificati aggiunti</string>
|
||||
<string name="app_settings_reset_certificates_success">Sono stati cancellati tutti i certificati aggiunti</string>
|
||||
<string name="app_settings_debug">Debug</string>
|
||||
<string name="app_settings_log_to_external_storage">Log su file esterno</string>
|
||||
<string name="app_settings_log_to_external_storage_on">Log su dispositivo esterno (se disponibile)</string>
|
||||
<string name="app_settings_log_to_external_storage_off">Log su file esterno disabilitato</string>
|
||||
<string name="app_settings_show_debug_info">Mostra informazioni di debug</string>
|
||||
<string name="app_settings_show_debug_info_details">Mostra e condividi i dettagli del programma e della configurazione</string>
|
||||
<!--AccountActivity-->
|
||||
<string name="account_synchronize_now">Sincronizza adesso</string>
|
||||
<string name="account_synchronizing_now">Sincronizzazione in corso</string>
|
||||
<string name="account_settings">Impostazioni account</string>
|
||||
<string name="account_rename">Rinomina account</string>
|
||||
<string name="account_rename_new_name">I dati locali non salvati potrebbero essere rimossi. È necessario effettuare la risincronizzazione dopo la rinomina. Nuovo nome dell\'account:</string>
|
||||
<string name="account_rename_rename">Rinomina</string>
|
||||
<string name="account_delete">Elimina account</string>
|
||||
<string name="account_delete_confirmation_title">Cancellare l\'account?</string>
|
||||
<string name="account_delete_confirmation_text">Tutte le copie locali delle rubriche, dei calendari e degli elenchi attività verranno eliminate.</string>
|
||||
<string name="account_carddav">CardDAV</string>
|
||||
<string name="account_caldav">CalDAV</string>
|
||||
<string name="account_webcal">Webcal</string>
|
||||
<string name="account_refresh_address_book_list">Aggiorna elenco degli indirizzari</string>
|
||||
<string name="account_create_new_address_book">Crea un nuovo indirizzario</string>
|
||||
<string name="account_refresh_calendar_list">Aggiorna lista calendari</string>
|
||||
<string name="account_create_new_calendar">Crea nuovo calendario</string>
|
||||
<string name="account_install_icsdroid">Installa ICSdroid</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">Permessi DAVdroid</string>
|
||||
<string name="permissions_calendar">Permessi calendario</string>
|
||||
<string name="permissions_calendar_details">Per sincronizzare gli eventi CalDAV con i calendari locali DAVdroid deve avere l\'accesso ai tuoi calendari.</string>
|
||||
<string name="permissions_calendar_request">Richiesta autorizzazione al calendario</string>
|
||||
<string name="permissions_contacts">Permessi Contatti</string>
|
||||
<string name="permissions_contacts_details">Per sincronizzare l\'indirizzario CardDAV con i contatti locali DAVdroid deve avere l\'accesso ai tuoi contatti.</string>
|
||||
<string name="permissions_contacts_request">Richiesta autorizzazione ai contatti</string>
|
||||
<string name="permissions_opentasks">Permessi OpenTasks</string>
|
||||
<string name="permissions_opentasks_details">Per sincronizzazione l\'elenco attività di CalDAV con l\'elenco locale DAVdroid deve avere l\'accesso ad OpenTasks.</string>
|
||||
<string name="permissions_opentasks_request">Richiesta autorizzazione ad OpenTasks</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_help_url">https://davdroid.bitfire.at/configuration/?pk_campaign=davdroid-app</string>
|
||||
<string name="login_title">Aggiungi account</string>
|
||||
<string name="login_type_email">Accedi con indirizzo email</string>
|
||||
<string name="login_email_address">Indirizzo email</string>
|
||||
<string name="login_email_address_error">È necessario un indirizzo email valido</string>
|
||||
<string name="login_password">Password</string>
|
||||
<string name="login_password_required">Password richiesta</string>
|
||||
<string name="login_type_url">Accedi con URL e nome utente</string>
|
||||
<string name="login_url_must_be_http_or_https">L\'URL deve iniziare con http(s)://</string>
|
||||
<string name="login_url_host_name_required">Nome host richiesto</string>
|
||||
<string name="login_user_name">Nome utente</string>
|
||||
<string name="login_user_name_required">Nome utente richiesto</string>
|
||||
<string name="login_base_url">Base URL</string>
|
||||
<string name="login_login">Login</string>
|
||||
<string name="login_back">Indietro</string>
|
||||
<string name="login_create_account">Crea account</string>
|
||||
<string name="login_account_name">Nome account</string>
|
||||
<string name="login_account_name_info">Inserisci il tuo indirizzo email come nome dell\'account in quanto Android userà il nome dell\'account nel campo ORGANIZER degli eventi creati. Non è possibile avere due account con nome uguale.</string>
|
||||
<string name="login_account_contact_group_method">Metodo del contact group:</string>
|
||||
<string name="login_account_name_required">Richiesto il nome dell\'account</string>
|
||||
<string name="login_account_not_created">L\'account non può essere creato</string>
|
||||
<string name="login_configuration_detection">Rilevazione configurazione</string>
|
||||
<string name="login_querying_server">Attendere, invio richiesta al server...</string>
|
||||
<string name="login_no_caldav_carddav">Impossibile trovare servizi CalDAV o CardDAV.</string>
|
||||
<string name="login_view_logs">Vedi i log</string>
|
||||
<!--AccountSettingsActivity-->
|
||||
<string name="settings_title">Impostazioni: %s</string>
|
||||
<string name="settings_authentication">Autenticazione</string>
|
||||
<string name="settings_username">Nome utente</string>
|
||||
<string name="settings_enter_username">Inserisci nome utente:</string>
|
||||
<string name="settings_password">Password</string>
|
||||
<string name="settings_password_summary">Aggiorna la password come sul tuo server.</string>
|
||||
<string name="settings_enter_password">Inserisci la tua password:</string>
|
||||
<string name="settings_sync">Sincronizzazione</string>
|
||||
<string name="settings_sync_interval_contacts">Intervallo sincr. Contatti</string>
|
||||
<string name="settings_sync_summary_manually">Solo manualmente</string>
|
||||
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Ogni %d minuti e a seguito di ogni cambiamento locale</string>
|
||||
<string name="settings_sync_interval_calendars">Intervallo sincr. calendari</string>
|
||||
<string name="settings_sync_interval_tasks">Intervallo sincr. attività</string>
|
||||
<string-array name="settings_sync_interval_names">
|
||||
<item>Solo manualmente</item>
|
||||
<item>Ogni 15 minuti</item>
|
||||
<item>Ogni 30 minuti</item>
|
||||
<item>Ogni ora</item>
|
||||
<item>Ogni 2 ore</item>
|
||||
<item>Ogni 4 ore</item>
|
||||
<item>Una volta al giorno</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">Sincr. solo tramite WiFi</string>
|
||||
<string name="settings_sync_wifi_only_on">La sincronizzazione è limitata alle connessioni WiFi</string>
|
||||
<string name="settings_sync_wifi_only_off">Il tipo di connessione non è preso in considerazione</string>
|
||||
<string name="settings_sync_wifi_only_ssids_message">Nomi (SSID) delle reti WiFi autorizzate separati da virgola (lascia vuoto per autorizzarle tutte)</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">Organizzazione dei gruppi di contatto</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string-array name="settings_contact_group_method_entries">
|
||||
<item>I gruppi sono VCards separate</item>
|
||||
<item>I gruppi sono categorie per ogni contatto</item>
|
||||
</string-array>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Limite di tempo per gli eventi trascorsi</string>
|
||||
<string name="settings_sync_time_range_past_none">Verranno sincronizzati tutti gli eventi</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="one">Eventi più vecchi di un giorno saranno ignorati</item>
|
||||
<item quantity="other">Eventi più vecchi di %d giorni saranno ignorati</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Eventi più vecchi di questo numero di giorni verranno ignorati(può anche essere 0). Lasciare in bianco per sincronizzare tutti gli eventi.</string>
|
||||
<string name="settings_manage_calendar_colors">Cambia il colore del calendario</string>
|
||||
<string name="settings_manage_calendar_colors_on">I colori dei calendari sono gestiti da DAVdroid</string>
|
||||
<string name="settings_manage_calendar_colors_off">I colori dei calendari non sono gestiti da DAVdroid</string>
|
||||
<!--collection management-->
|
||||
<string name="create_addressbook">Crea indirizzario</string>
|
||||
<string name="create_addressbook_display_name_hint">Il mio indirizzario</string>
|
||||
<string name="create_calendar">Crea raccolta CalDAV</string>
|
||||
<string name="create_calendar_display_name_hint">Mio calendario</string>
|
||||
<string name="create_calendar_time_zone">Fuso orario:</string>
|
||||
<string name="create_calendar_type">Tipo di raccolta:</string>
|
||||
<string name="create_calendar_type_only_events">Calendario (solo eventi)</string>
|
||||
<string name="create_calendar_type_only_tasks">Elenco attività (solo attività)</string>
|
||||
<string name="create_calendar_type_events_and_tasks">Combinato (eventi e attività)</string>
|
||||
<string name="create_collection_color">Imposta colore della raccolta</string>
|
||||
<string name="create_collection_creating">Crea una raccolta</string>
|
||||
<string name="create_collection_display_name">Mostra il nome (titolo) di questa raccolta:</string>
|
||||
<string name="create_collection_display_name_required">Il titolo è richiesto</string>
|
||||
<string name="create_collection_description">Descrizione (opzionale):</string>
|
||||
<string name="create_collection_home_set">Imposta la home:</string>
|
||||
<string name="create_collection_create">Crea</string>
|
||||
<string name="delete_collection">Elimina raccolta</string>
|
||||
<string name="delete_collection_confirm_title">Sei sicuro?</string>
|
||||
<string name="delete_collection_confirm_warning">Questa raccolta (%s) e tutti i suoi dati saranno rimossi dal server.</string>
|
||||
<string name="delete_collection_deleting_collection">Cancellazione della raccolta</string>
|
||||
<!--ExceptionInfoFragment-->
|
||||
<string name="exception">Si è verificato un errore.</string>
|
||||
<string name="exception_httpexception">Si è verificato un errore HTTP.</string>
|
||||
<string name="exception_ioexception">Si è verificato un errore di I/O.</string>
|
||||
<string name="exception_show_details">Mostra dettagli</string>
|
||||
<!--sync adapters and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Informazioni di debug</string>
|
||||
<string name="sync_error_permissions">Autorizzazioni DAVdroid</string>
|
||||
<string name="sync_error_permissions_text">Autorizzazioni addizionali richieste</string>
|
||||
<string name="sync_error_calendar">Sincronizzazione del calendario fallita (%s)</string>
|
||||
<string name="sync_error_contacts">Sincronizzazione dell\'indirizzario fallita (%s)</string>
|
||||
<string name="sync_error_tasks">Sincronizzazione delle attività fallita (%s)</string>
|
||||
<string name="sync_error">Errore nel %s</string>
|
||||
<string name="sync_error_http_dav">Errore del server nel %s</string>
|
||||
<string name="sync_error_local_storage">Errore del database nel %s</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>inizio sincronizzazione</item>
|
||||
<item>controllo caratteristiche del server</item>
|
||||
<item>elaborazione voci cancellate in locale</item>
|
||||
<item>elaborazione voci create o modificate</item>
|
||||
<item>invio voci create o modificate</item>
|
||||
<item>controllo stato della sincronizzazione</item>
|
||||
<item>elenco voci locali</item>
|
||||
<item>elenco voci remote</item>
|
||||
<item>confronto voci locali e remote</item>
|
||||
<item>download voci remote</item>
|
||||
<item>post-processing</item>
|
||||
<item>salvataggio stato della sincronizzazione</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">Nome utente o password errati</string>
|
||||
<!--cert4android-->
|
||||
<string name="certificate_notification_connection_security">DAVdroid: sicurezza della connessione</string>
|
||||
<string name="trust_certificate_unknown_certificate_found">DAVdroid ha trovato un certificato sconosciuto. Ritenerlo affidabile?</string>
|
||||
</resources>
|
||||
249
app/src/davdroid/res/values-ja/strings.xml
Normal file
249
app/src/davdroid/res/values-ja/strings.xml
Normal file
@@ -0,0 +1,249 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!--common strings-->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="account_title_address_book">DAVdroid アドレス帳</string>
|
||||
<string name="address_books_authority_title">アドレス帳</string>
|
||||
<string name="help">ヘルプ</string>
|
||||
<string name="manage_accounts">アカウントの管理</string>
|
||||
<string name="please_wait">しばらくお待ちください …</string>
|
||||
<string name="send">送信</string>
|
||||
<string name="homepage_url">https://davdroid.bitfire.at/?pk_campaign=davdroid-app</string>
|
||||
<!--startup dialogs-->
|
||||
<string name="startup_battery_optimization">バッテリー最適化</string>
|
||||
<string name="startup_battery_optimization_message">Android は数日後に DAVdroid の同期を無効にする/減らすことがあります。これを防止するには、バッテリー最適化をオフにしてください。</string>
|
||||
<string name="startup_battery_optimization_disable">DAVdroid 用にオフにする</string>
|
||||
<string name="startup_dont_show_again">次回から表示しない</string>
|
||||
<string name="startup_donate">オープンソース情報</string>
|
||||
<string name="startup_donate_message">あなたがオープンソース ソフトウェア (GPLv3) の DAVdroid を使用していただくことに、私たちは満足しています。 DAVdroid の開発はハードワークで、何千もの作業時間がかかりました。寄付をご検討ください。</string>
|
||||
<string name="startup_donate_now">寄付ページを表示</string>
|
||||
<string name="startup_donate_later">たぶん後で</string>
|
||||
<string name="startup_google_play_accounts_removed">Play ストア DRM バグ情報</string>
|
||||
<string name="startup_google_play_accounts_removed_message">特定の条件下で、DAVdroid を再起動後またはアップグレードした後、Play ストア DRM によりすべての DAVdroid アカウントがなくなる可能性があります。この問題の影響を受けている場合 (のみ)、Play ストアから「DAVdroid JB 回避策」をインストールしてください。</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">追加情報</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks がインストールされていません</string>
|
||||
<string name="startup_opentasks_not_installed_message">OpenTasks アプリが利用できないため、DAVdroid はタスクリストを同期することができません。</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">OpenTasks をインストールした後で、DAVdroidを再インストールして、再度アカウントを追加してください (Android のバグ)。</string>
|
||||
<string name="startup_opentasks_not_installed_install">OpenTasks をインストール</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">ライセンス規約</string>
|
||||
<string name="about_license_info_no_warranty">このプログラムは完全に無保証で提供されます。これはフリーソフトウェアで、特定の条件下での再頒布を歓迎します。</string>
|
||||
<!--global settings-->
|
||||
<string name="logging_davdroid_file_logging">DAVdroid ファイルログ</string>
|
||||
<string name="logging_to_external_storage">外部ストレージにログ: %s</string>
|
||||
<string name="logging_couldnt_create_file">外部ログファイルを作成できませんでした: %s</string>
|
||||
<string name="logging_no_external_storage">外部ストレージが見つかりません</string>
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_open">ナビゲーションドロワーを開く</string>
|
||||
<string name="navigation_drawer_close">ナビゲーションドロワーを閉じる</string>
|
||||
<string name="navigation_drawer_subtitle">CalDAV/CardDAV 同期アダプター</string>
|
||||
<string name="navigation_drawer_about">アプリについて / ライセンス</string>
|
||||
<string name="navigation_drawer_settings">設定</string>
|
||||
<string name="navigation_drawer_news_updates">ニュース & 更新</string>
|
||||
<string name="navigation_drawer_external_links">外部リンク</string>
|
||||
<string name="navigation_drawer_website">Web サイト</string>
|
||||
<string name="navigation_drawer_faq">FAQ</string>
|
||||
<string name="navigation_drawer_faq_url">https://davdroid.bitfire.at/faq/?pk_campaign=davdroid-app</string>
|
||||
<string name="navigation_drawer_forums">ヘルプ / フォーラム</string>
|
||||
<string name="navigation_drawer_donate">寄付</string>
|
||||
<string name="account_list_empty">DAVdroid にようこそ!\n\nCalDAV/CardDAV アカウントを追加できるようになりました。</string>
|
||||
<string name="accounts_global_sync_disabled">システム全体の自動同期が無効です</string>
|
||||
<string name="accounts_global_sync_enable">有効</string>
|
||||
<!--DavService-->
|
||||
<string name="dav_service_refresh_failed">サービスの検出に失敗しました</string>
|
||||
<string name="dav_service_refresh_couldnt_refresh">コレクション リストを更新できません</string>
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">設定</string>
|
||||
<string name="app_settings_user_interface">ユーザーインターフェイス</string>
|
||||
<string name="app_settings_reset_hints">ヒントをリセット</string>
|
||||
<string name="app_settings_reset_hints_summary">以前非表示にしたヒントを、再度有効にします</string>
|
||||
<string name="app_settings_reset_hints_success">すべてのヒントが再度表示されます</string>
|
||||
<string name="app_settings_connection">接続</string>
|
||||
<string name="app_settings_override_proxy">プロキシ設定を上書き</string>
|
||||
<string name="app_settings_override_proxy_on">カスタムのプロキシ設定を使用する</string>
|
||||
<string name="app_settings_override_proxy_off">システムデフォルトのプロキシ設定を使用する</string>
|
||||
<string name="app_settings_override_proxy_host">HTTP プロキシのホスト名</string>
|
||||
<string name="app_settings_override_proxy_port">HTTP プロキシのポート</string>
|
||||
<string name="app_settings_security">セキュリティ</string>
|
||||
<string name="app_settings_distrust_system_certs">システム証明書の信頼を解除</string>
|
||||
<string name="app_settings_distrust_system_certs_on">システムとユーザーが追加したCAを信頼しない</string>
|
||||
<string name="app_settings_distrust_system_certs_off">システムとユーザーが追加したCAを信頼する (推奨)</string>
|
||||
<string name="app_settings_reset_certificates">(未)信頼済証明書をリセット</string>
|
||||
<string name="app_settings_reset_certificates_summary">すべてのカスタム証明書の信頼をリセット</string>
|
||||
<string name="app_settings_reset_certificates_success">すべてのカスタム証明書をクリアしました</string>
|
||||
<string name="app_settings_debug">デバッグ</string>
|
||||
<string name="app_settings_log_to_external_storage">外部ファイルにログ出力</string>
|
||||
<string name="app_settings_log_to_external_storage_on">(可能な場合) 外部ストレージにログ出力します</string>
|
||||
<string name="app_settings_log_to_external_storage_off">外部ファイルログ出力は無効です</string>
|
||||
<string name="app_settings_show_debug_info">デバッグ情報を表示</string>
|
||||
<string name="app_settings_show_debug_info_details">ソフトウェアと設定の詳細を表示/共有</string>
|
||||
<!--AccountActivity-->
|
||||
<string name="account_synchronize_now">今すぐ同期</string>
|
||||
<string name="account_synchronizing_now">同期中</string>
|
||||
<string name="account_settings">アカウント設定</string>
|
||||
<string name="account_rename">アカウントの名前を変更</string>
|
||||
<string name="account_rename_new_name">未保存のローカルデータが破棄されることがあります。 名前の変更後に再同期が必要です。 新しいアカウント名:</string>
|
||||
<string name="account_rename_rename">名前を変更</string>
|
||||
<string name="account_delete">アカウントを削除</string>
|
||||
<string name="account_delete_confirmation_title">アカウントを削除してもよろしいですか?</string>
|
||||
<string name="account_delete_confirmation_text">アドレス帳、カレンダー、タスクリストのローカルコピーがすべて削除されます。</string>
|
||||
<string name="account_carddav">CardDAV</string>
|
||||
<string name="account_caldav">CalDAV</string>
|
||||
<string name="account_webcal">Webcal</string>
|
||||
<string name="account_refresh_address_book_list">アドレス帳リストを更新</string>
|
||||
<string name="account_create_new_address_book">新しいアドレス帳を作成</string>
|
||||
<string name="account_refresh_calendar_list">カレンダーリストを更新</string>
|
||||
<string name="account_create_new_calendar">新しいカレンダーを作成</string>
|
||||
<string name="account_no_webcal_handler_found">Webcal に対応するアプリが見つかりません</string>
|
||||
<string name="account_install_icsdroid">ICSdroid をインストール</string>
|
||||
<string name="account_read_only_address_book_selected">読み取り専用のアドレス帳 – ローカルの変更は破棄されます!</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">DAVdroid アクセス許可</string>
|
||||
<string name="permissions_calendar">カレンダー アクセス許可</string>
|
||||
<string name="permissions_calendar_details">ローカルのカレンダーと CalDAV イベントを同期するため、DAVdroid がカレンダーにアクセスする必要があります。</string>
|
||||
<string name="permissions_calendar_request">カレンダー アクセス許可の要求</string>
|
||||
<string name="permissions_contacts">連絡先アクセス許可</string>
|
||||
<string name="permissions_contacts_details">ローカルの連絡先と CalDAV アドレス帳を同期するため、DAVdroid が連絡先にアクセスする必要があります。</string>
|
||||
<string name="permissions_contacts_request">連絡先アクセス許可の要求</string>
|
||||
<string name="permissions_opentasks">OpenTasks アクセス許可</string>
|
||||
<string name="permissions_opentasks_details">ローカルのタスクリストと CalDAV タスクを同期するため、DAVdroid が OpenTasks にアクセスする必要があります。</string>
|
||||
<string name="permissions_opentasks_request">OpenTasks アクセス許可の要求</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_help_url">https://davdroid.bitfire.at/configuration/?pk_campaign=davdroid-app</string>
|
||||
<string name="login_title">アカウントを追加</string>
|
||||
<string name="login_type_email">メールアドレスでログイン</string>
|
||||
<string name="login_email_address">メールアドレス</string>
|
||||
<string name="login_email_address_error">有効なメールアドレスが必要です</string>
|
||||
<string name="login_password">パスワード</string>
|
||||
<string name="login_password_required">パスワードが必要です</string>
|
||||
<string name="login_type_url">URL とユーザー名でログイン</string>
|
||||
<string name="login_url_must_be_http_or_https">URL は http(s):// で始まる必要があります</string>
|
||||
<string name="login_url_host_name_required">ホスト名が必要です</string>
|
||||
<string name="login_user_name">ユーザー名</string>
|
||||
<string name="login_user_name_required">ユーザー名が必要です</string>
|
||||
<string name="login_base_url">ベース URL</string>
|
||||
<string name="login_login">ログイン</string>
|
||||
<string name="login_back">戻る</string>
|
||||
<string name="login_create_account">アカウントを作成</string>
|
||||
<string name="login_account_name">アカウント名</string>
|
||||
<string name="login_account_name_info">Android はあなたが作成した予定の ORGANIZER フィールドとしてアカウント名を使用するので、アカウント名としてメールアドレスを使用してください。同じ名前のアカウントを 2 つ持つことはできません。</string>
|
||||
<string name="login_account_contact_group_method">連絡先グループ方法:</string>
|
||||
<string name="login_account_name_required">アカウント名が必要です</string>
|
||||
<string name="login_account_not_created">アカウントを作成できません</string>
|
||||
<string name="login_configuration_detection">設定の検出</string>
|
||||
<string name="login_querying_server">しばらくお待ちください。サーバーに問い合わせ中…</string>
|
||||
<string name="login_no_caldav_carddav">CalDAV または CardDAV サービスが見つかりません。</string>
|
||||
<string name="login_view_logs">ログを表示</string>
|
||||
<!--AccountSettingsActivity-->
|
||||
<string name="settings_title">設定: %s</string>
|
||||
<string name="settings_authentication">認証</string>
|
||||
<string name="settings_username">ユーザー名</string>
|
||||
<string name="settings_enter_username">ユーザー名を入力:</string>
|
||||
<string name="settings_password">パスワード</string>
|
||||
<string name="settings_password_summary">ご利用のサーバーに従ってパスワードを更新します。</string>
|
||||
<string name="settings_enter_password">パスワードを入力:</string>
|
||||
<string name="settings_sync">同期</string>
|
||||
<string name="settings_sync_interval_contacts">連絡先同期間隔</string>
|
||||
<string name="settings_sync_summary_manually">手動のみ</string>
|
||||
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">%d 分ごと + ローカルの変更時はすぐに</string>
|
||||
<string name="settings_sync_interval_calendars">カレンダー同期間隔</string>
|
||||
<string name="settings_sync_interval_tasks">タスク同期間隔</string>
|
||||
<string-array name="settings_sync_interval_names">
|
||||
<item>手動のみ</item>
|
||||
<item>15 分ごと</item>
|
||||
<item>30 分ごと</item>
|
||||
<item>1 時間ごと</item>
|
||||
<item>2 時間ごと</item>
|
||||
<item>4 時間ごと</item>
|
||||
<item>1 日 1 回</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">WiFi でのみ同期</string>
|
||||
<string name="settings_sync_wifi_only_on">同期は WiFi 接続に制限されます</string>
|
||||
<string name="settings_sync_wifi_only_off">接続の種類は考慮されません</string>
|
||||
<string name="settings_sync_wifi_only_ssids">WiFi SSID 制限</string>
|
||||
<string name="settings_sync_wifi_only_ssids_on">%s でのみ同期します</string>
|
||||
<string name="settings_sync_wifi_only_ssids_off">すべての WiFi 接続が使用されます</string>
|
||||
<string name="settings_sync_wifi_only_ssids_message">利用可能な WiFi ネットワークのカンマ区切りの名前 (SSID) (空白にするとすべて)</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">連絡先グループ方法</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string-array name="settings_contact_group_method_entries">
|
||||
<item>グループは個別の VCards</item>
|
||||
<item>グループは連絡先カテゴリーごと</item>
|
||||
</string-array>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">過去イベントの時間限度</string>
|
||||
<string name="settings_sync_time_range_past_none">すべてのイベントが同期されます</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="other">%d 日より前のイベントは無視されます</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">この日数より過去のイベントは無視されます (0 も可)。すべてのイベントを同期させるには、空白のままにしてください。</string>
|
||||
<string name="settings_manage_calendar_colors">カレンダーの色を管理</string>
|
||||
<string name="settings_manage_calendar_colors_on">カレンダーの色は DAVdroid が管理します</string>
|
||||
<string name="settings_manage_calendar_colors_off">カレンダーの色を DAVdroid が設定しません</string>
|
||||
<string name="settings_event_colors">イベントカラーサポート</string>
|
||||
<string name="settings_event_colors_on">イベントカラーを同期</string>
|
||||
<string name="settings_event_colors_off">イベントカラーを同期しない</string>
|
||||
<string name="settings_event_colors_off_confirm">イベントカラーをオフにすると、すでに同期しているイベントカラーが削除されることがあります。</string>
|
||||
<!--collection management-->
|
||||
<string name="create_addressbook">アドレス帳を作成</string>
|
||||
<string name="create_addressbook_display_name_hint">マイ アドレス帳</string>
|
||||
<string name="create_calendar">CalDAV コレクションを作成</string>
|
||||
<string name="create_calendar_display_name_hint">マイ カレンダー</string>
|
||||
<string name="create_calendar_time_zone">タイムゾーン:</string>
|
||||
<string name="create_calendar_type">コレクションの種類:</string>
|
||||
<string name="create_calendar_type_only_events">カレンダー (イベントのみ)</string>
|
||||
<string name="create_calendar_type_only_tasks">タスクリスト (タスクのみ)</string>
|
||||
<string name="create_calendar_type_events_and_tasks">組み合わせ (イベントとタスク)</string>
|
||||
<string name="create_collection_color">コレクションの色を設定</string>
|
||||
<string name="create_collection_creating">コレクションの作成中</string>
|
||||
<string name="create_collection_display_name">このコレクションの表示名 (タイトル):</string>
|
||||
<string name="create_collection_display_name_required">タイトルが必要です</string>
|
||||
<string name="create_collection_description">説明 (オプション):</string>
|
||||
<string name="create_collection_home_set">ホーム セット:</string>
|
||||
<string name="create_collection_create">作成</string>
|
||||
<string name="delete_collection">コレクションを削除</string>
|
||||
<string name="delete_collection_confirm_title">よろしいですか?</string>
|
||||
<string name="delete_collection_confirm_warning">このコレクション (%s) とそのすべてのデータがサーバーから削除されます。</string>
|
||||
<string name="delete_collection_deleting_collection">コレクションの削除中</string>
|
||||
<!--ExceptionInfoFragment-->
|
||||
<string name="exception">エラーが発生しました。</string>
|
||||
<string name="exception_httpexception">HTTP エラーが発生しました。</string>
|
||||
<string name="exception_ioexception">I/O エラーが発生しました。</string>
|
||||
<string name="exception_show_details">詳細を表示</string>
|
||||
<!--sync adapters and DebugInfoActivity-->
|
||||
<string name="debug_info_title">デバッグ情報</string>
|
||||
<string name="sync_contacts_read_only_address_book">読み取り専用のアドレス帳</string>
|
||||
<plurals name="sync_contacts_local_contact_changes_discarded">
|
||||
<item quantity="other">%d ローカル連絡先の変更が破棄されました</item>
|
||||
</plurals>
|
||||
<string name="sync_error_permissions">DAVdroid アクセス許可</string>
|
||||
<string name="sync_error_permissions_text">追加のアクセス許可が必要です</string>
|
||||
<string name="sync_error_calendar">カレンダーの同期に失敗しました (%s)</string>
|
||||
<string name="sync_error_contacts">アドレス帳の同期に失敗しました (%s)</string>
|
||||
<string name="sync_error_tasks">タスクの同期に失敗しました (%s)</string>
|
||||
<string name="sync_error">%s 時にエラー</string>
|
||||
<string name="sync_error_http_dav">%s 時にサーバーエラー</string>
|
||||
<string name="sync_error_local_storage">%s 時にデータベースエラー</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>同期の準備中</item>
|
||||
<item>機能の問い合わせ中</item>
|
||||
<item>ローカルで削除されたエントリーの処理中</item>
|
||||
<item>作成済/更新済エントリーの準備中</item>
|
||||
<item>作成済/更新済エントリーのアップロード中</item>
|
||||
<item>同期の状態を確認中</item>
|
||||
<item>ローカルのエントリーをリスト中</item>
|
||||
<item>リモートのエントリーをリスト中</item>
|
||||
<item>ローカル/リモートのエントリーを比較中</item>
|
||||
<item>リモートのエントリーをダウンロード中</item>
|
||||
<item>後処理中</item>
|
||||
<item>同期の状態を保存中</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">ユーザー名/パスワードが間違っています</string>
|
||||
<!--cert4android-->
|
||||
<string name="certificate_notification_connection_security">DAVdroid: 接続セキュリティ</string>
|
||||
<string name="trust_certificate_unknown_certificate_found">DAVdroidは、未知の証明書を検出しました。それを信頼しますか?</string>
|
||||
</resources>
|
||||
252
app/src/davdroid/res/values-nb-rNO/strings.xml
Normal file
252
app/src/davdroid/res/values-nb-rNO/strings.xml
Normal file
@@ -0,0 +1,252 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!--common strings-->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="account_title_address_book">DAVdroid-adressebok</string>
|
||||
<string name="address_books_authority_title">Adressebøker</string>
|
||||
<string name="help">Hjelp</string>
|
||||
<string name="manage_accounts">Behandle kontoer</string>
|
||||
<string name="please_wait">Vent…</string>
|
||||
<string name="send">Send</string>
|
||||
<string name="homepage_url">https://davdroid.bitfire.at/?pk_campaign=davdroid-app</string>
|
||||
<!--startup dialogs-->
|
||||
<string name="startup_battery_optimization">Batterioptimisering</string>
|
||||
<string name="startup_battery_optimization_message">Det kan hende Android skrur av/reduserer DAVdroid-synkronisering etter et par dager. For å forhindre dette, skru av batterioptimisering.</string>
|
||||
<string name="startup_battery_optimization_disable">Skru av for DAVdroid</string>
|
||||
<string name="startup_dont_show_again">Ikke vis igjen</string>
|
||||
<string name="startup_donate">Friprog-informasjon</string>
|
||||
<string name="startup_donate_message">Vi er glade for at du bruker DAVdroid, som er fri programvare (GPLv3). Siden utvikling av DAVdroid er hardt arbeid og har tatt tusenvis av arbeidstimer, bes du overveie en donasjon.</string>
|
||||
<string name="startup_donate_now">Vis donasjonsside</string>
|
||||
<string name="startup_donate_later">Kanskje senere</string>
|
||||
<string name="startup_google_play_accounts_removed">DRM-feilinformasjon på Play-butikken</string>
|
||||
<string name="startup_google_play_accounts_removed_message">Under gitte forhold vil DRM fra Play-butikken forårsake at alle DAVdroid-kontoer forsvinner etter omstart eller etter oppgradering av DAVdroid. Hvis du rammes av dette problemet (og bare da), installer \"DAVdroid JB Workaround\" fra Play-butikken.</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Mer informasjon</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks er ikke installert</string>
|
||||
<string name="startup_opentasks_not_installed_message">OpenTasks-programmet er ikke tilgjengelig, så DAVdroid kan ikke synkronisere gjøremålslister.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Etter å ha installert OpenTasks, må du reinstallere Davdroid og legge til kontoene dine igjen (Android-feil).</string>
|
||||
<string name="startup_opentasks_not_installed_install">Installer OpenTasks</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Lisensvilkår</string>
|
||||
<string name="about_license_info_no_warranty">Dette programmet kommer uten NOEN FORM FOR GARANTI. Det er fri programvare, og du er velkommen til å redistribuere det under gitte forhold.</string>
|
||||
<!--global settings-->
|
||||
<string name="logging_davdroid_file_logging">DAVDroid fil-logging</string>
|
||||
<string name="logging_to_external_storage">Logger til ekstern lagringsmedium: %s</string>
|
||||
<string name="logging_couldnt_create_file">Kan ikke opprette ekstern loggfil: %s</string>
|
||||
<string name="logging_no_external_storage">Fant ingen ekstern lagringsplass</string>
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_open">Åpne navigasjonsskuff</string>
|
||||
<string name="navigation_drawer_close">Lukk navigasjonsskuff</string>
|
||||
<string name="navigation_drawer_subtitle">CalDAV/CardDAV -synkroniseringsadapter</string>
|
||||
<string name="navigation_drawer_about">Om / Lisens</string>
|
||||
<string name="navigation_drawer_beta_feedback">Tilbakemelding om beta-en</string>
|
||||
<string name="navigation_drawer_settings">Innstillinger</string>
|
||||
<string name="navigation_drawer_news_updates">Nyheter og oppdateringer</string>
|
||||
<string name="navigation_drawer_external_links">Eksterne lenker</string>
|
||||
<string name="navigation_drawer_website">Nettside</string>
|
||||
<string name="navigation_drawer_faq">O-S-S</string>
|
||||
<string name="navigation_drawer_faq_url">https://davdroid.bitfire.at/faq/?pk_campaign=davdroid-app</string>
|
||||
<string name="navigation_drawer_forums">Hjelp / Forum</string>
|
||||
<string name="navigation_drawer_donate">Doner</string>
|
||||
<string name="account_list_empty">Velkommen til DAVdroid.\n\nDu kan legge til en CalDAV/CardDAV-konto nå.</string>
|
||||
<string name="accounts_global_sync_disabled">Systemomspennende automatisk synkronisering avskrudd</string>
|
||||
<string name="accounts_global_sync_enable">Skru på</string>
|
||||
<!--DavService-->
|
||||
<string name="dav_service_refresh_failed">Tjenesteoppdagelse mislyktes</string>
|
||||
<string name="dav_service_refresh_couldnt_refresh">Kunne ikke gjenoppfriske innsamlingsliste</string>
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">Innstillinger</string>
|
||||
<string name="app_settings_user_interface">Brukergrensesnitt</string>
|
||||
<string name="app_settings_reset_hints">Tilbakestill hint</string>
|
||||
<string name="app_settings_reset_hints_summary">Skrur på hint som har blitt avslått tidligere</string>
|
||||
<string name="app_settings_reset_hints_success">Alle hint vil bli vist igjen</string>
|
||||
<string name="app_settings_connection">Tilkobling</string>
|
||||
<string name="app_settings_override_proxy">Overstyr mellomtjenerinnstillinger</string>
|
||||
<string name="app_settings_override_proxy_on">Bruk egendefinerte mellomtjenerinnstillinger</string>
|
||||
<string name="app_settings_override_proxy_off">Bruk systemets forvalgte mellomtjenerinnstillinger</string>
|
||||
<string name="app_settings_override_proxy_host">Vertsnavn for HTTP-mellomtjener</string>
|
||||
<string name="app_settings_override_proxy_port">HTTP-mellomtjeningsport</string>
|
||||
<string name="app_settings_security">Sikkerhet</string>
|
||||
<string name="app_settings_distrust_system_certs">Fjern tiltro til systemsertifikater</string>
|
||||
<string name="app_settings_distrust_system_certs_on">System og brukertillagte sertifikatsmyntigheter vil ikke bli tiltrodd</string>
|
||||
<string name="app_settings_distrust_system_certs_off">System- og bruker -tillagte sertifikatsmyndigheter vil bli tiltrodd (anbefalt)</string>
|
||||
<string name="app_settings_reset_certificates">Tilbakestill (ikke)tiltrodde sertifikater</string>
|
||||
<string name="app_settings_reset_certificates_summary">Tilbakestiller tillit til alle egendefinerte sertifikater</string>
|
||||
<string name="app_settings_reset_certificates_success">Alle egendefinerte sertifikater har blitt fjernet</string>
|
||||
<string name="app_settings_debug">Feilretting</string>
|
||||
<string name="app_settings_log_to_external_storage">Logg til ekstern fil</string>
|
||||
<string name="app_settings_log_to_external_storage_on">Logger til eksternt lagringsmedium (hvis tilgjengelig)</string>
|
||||
<string name="app_settings_log_to_external_storage_off">Ekstern fillogging har blitt skrudd av</string>
|
||||
<string name="app_settings_show_debug_info">Vis feilrettingsinfo</string>
|
||||
<string name="app_settings_show_debug_info_details">Vis/del programvare- og oppsettsdetaljer</string>
|
||||
<!--AccountActivity-->
|
||||
<string name="account_synchronize_now">Synkroniser nå</string>
|
||||
<string name="account_synchronizing_now">Synkroniserer nå</string>
|
||||
<string name="account_settings">Kontoinnstillinger</string>
|
||||
<string name="account_rename">Gi konto nytt navn</string>
|
||||
<string name="account_rename_new_name">Ulagret lokal data kan bli avslått. Ny synkronisering kreves etter ny navngivning. Nytt kontonavn:</string>
|
||||
<string name="account_rename_rename">Gi nytt navn</string>
|
||||
<string name="account_delete">Slett konto</string>
|
||||
<string name="account_delete_confirmation_title">Vil du virkeling slette kontoen?</string>
|
||||
<string name="account_delete_confirmation_text">Alle lokale kopier av adressebøker, kalendere og gjøremålslister vil bli slettet.</string>
|
||||
<string name="account_carddav">CardDAV</string>
|
||||
<string name="account_caldav">CalDAV</string>
|
||||
<string name="account_webcal">Webcal</string>
|
||||
<string name="account_refresh_address_book_list">Gjenoppfrisk adressebokliste</string>
|
||||
<string name="account_create_new_address_book">Opprett ny adressebok</string>
|
||||
<string name="account_refresh_calendar_list">Gjenoppfrisk kalenderliste</string>
|
||||
<string name="account_create_new_calendar">Opprett ny kalender</string>
|
||||
<string name="account_no_webcal_handler_found">Fant ingen programmer med støtte for Webcal</string>
|
||||
<string name="account_install_icsdroid">Installer ICSdroid</string>
|
||||
<string name="account_read_only_address_book_selected">Adressebok uten skrivemuligheter - lokale endringer vil bli forkastet!</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">DAVdroid-tilgang</string>
|
||||
<string name="permissions_calendar">Kalender-tilgang</string>
|
||||
<string name="permissions_calendar_details">For å synkronisere CalDAV-hendelser med dine lokale kalendere må DAVdroid ha tilgang til kalenderne dine.</string>
|
||||
<string name="permissions_calendar_request">Forespør kalender-tilganger</string>
|
||||
<string name="permissions_contacts">Kontakt-tilgang</string>
|
||||
<string name="permissions_contacts_details">For å synkronisere CardDAV-adressebøker med dine lokale kontakter må DAVdroid ha tilgang til dine kontakter.</string>
|
||||
<string name="permissions_contacts_request">Forespør kontakt-tilgang</string>
|
||||
<string name="permissions_opentasks">OpenTasks-tilganger</string>
|
||||
<string name="permissions_opentasks_details">For å synkronisere CalDAV-gjøremål med din lokale gjøremålslister må DAVdroid ha tilgang til OpenTasks.</string>
|
||||
<string name="permissions_opentasks_request">Forespør OpenTasks-tilganger</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_help_url">https://davdroid.bitfire.at/configuration/?pk_campaign=davdroid-app</string>
|
||||
<string name="login_title">Legg til konto</string>
|
||||
<string name="login_type_email">Innlogging med e-postadresse</string>
|
||||
<string name="login_email_address">E-postadresse</string>
|
||||
<string name="login_email_address_error">Gyldig e-postadresse påkrevd</string>
|
||||
<string name="login_password">Passord</string>
|
||||
<string name="login_password_required">Passord kreves</string>
|
||||
<string name="login_type_url">Logg inn med nettadresse og brukernavn</string>
|
||||
<string name="login_url_must_be_http_or_https">Nettadresse må begynned med http(s)://</string>
|
||||
<string name="login_url_host_name_required">Vertsnavn kreves</string>
|
||||
<string name="login_user_name">Brukernavn</string>
|
||||
<string name="login_user_name_required">Brukernavn påkrevd</string>
|
||||
<string name="login_base_url">Landings-nettadresse</string>
|
||||
<string name="login_login">Logg inn</string>
|
||||
<string name="login_back">Tilbake</string>
|
||||
<string name="login_create_account">Opprett konto</string>
|
||||
<string name="login_account_name">Kontonavn</string>
|
||||
<string name="login_account_name_info">Bruk din e-postadresse som kontonavn fordi Android vil bruke kontonavnet som ORGANISATOR-felt for hendelser du oppretter. Du kan ikke ha to kontoer med samme navn.</string>
|
||||
<string name="login_account_contact_group_method">Kontaktgruppemetode:</string>
|
||||
<string name="login_account_name_required">Kontonavn påkrevd</string>
|
||||
<string name="login_account_not_created">Kontonavnet kan ikke opprettes</string>
|
||||
<string name="login_configuration_detection">Oppdagelse av oppsett</string>
|
||||
<string name="login_querying_server">Vent, spør tjener…</string>
|
||||
<string name="login_no_caldav_carddav">Fant ikke CalDAV eller CardDAV-tjeneste.</string>
|
||||
<string name="login_view_logs">Vis logger</string>
|
||||
<!--AccountSettingsActivity-->
|
||||
<string name="settings_title">Innstillinger: %s</string>
|
||||
<string name="settings_authentication">Identitetsbekreftelse</string>
|
||||
<string name="settings_username">Brukernavn</string>
|
||||
<string name="settings_enter_username">Skriv inn brukernavn:</string>
|
||||
<string name="settings_password">Passord</string>
|
||||
<string name="settings_password_summary">Oppdater passordet i henhold til din tjener.</string>
|
||||
<string name="settings_enter_password">Skriv inn passordet ditt:</string>
|
||||
<string name="settings_sync">Synkronisering</string>
|
||||
<string name="settings_sync_interval_contacts">Intervall for kontaktsynkronisering</string>
|
||||
<string name="settings_sync_summary_manually">Åpne manuelt</string>
|
||||
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Hvert %d minutt + umiddelbart ved lokale endringer</string>
|
||||
<string name="settings_sync_interval_calendars">Kalendersynkroniseringsintervall</string>
|
||||
<string name="settings_sync_interval_tasks">Gjøremålssynkroniseringsintervall</string>
|
||||
<string-array name="settings_sync_interval_names">
|
||||
<item>Bare manuelt</item>
|
||||
<item>Hvert kvarter</item>
|
||||
<item>Hver halvtime</item>
|
||||
<item>Hver time</item>
|
||||
<item>Hver andre time</item>
|
||||
<item>Hver fjerde time</item>
|
||||
<item>Én gang om dagen</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">Bare synk. over Wi-Fi</string>
|
||||
<string name="settings_sync_wifi_only_on">Synkronisering er begrenset til Wi-Fi -tilkoblinger</string>
|
||||
<string name="settings_sync_wifi_only_off">Tilkoblingstypen blir ikke tatt i betraktning</string>
|
||||
<string name="settings_sync_wifi_only_ssids">Wi-Fi SSID -begrensning</string>
|
||||
<string name="settings_sync_wifi_only_ssids_on">Vil kun synkronisere over %s</string>
|
||||
<string name="settings_sync_wifi_only_ssids_off">Alle Wi-Fi -tilkoblinger vil bli brukt</string>
|
||||
<string name="settings_sync_wifi_only_ssids_message">Kommainndelte navn (SSID-er) på tillatte Wi-Fi -nettverk (la stå tomt for alle)</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">Kontaktgruppemetode</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string-array name="settings_contact_group_method_entries">
|
||||
<item>Grupper er egne vKort</item>
|
||||
<item>Grupper er kategorier per kontakt</item>
|
||||
</string-array>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Tidsgrense for tidligere hendelser</string>
|
||||
<string name="settings_sync_time_range_past_none">Alle gjøremål vil bli synkronisert</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="one">Gjøremål for mer enn én dag siden vil bli sett bort fra</item>
|
||||
<item quantity="other">Gjøremål for mer enn %d dager siden vil bli sett bort fra</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Hendelser som er mer enn dette antallet dager i fortid vil bli ignorert (kan være 0). La stå tomt for å synkronisere alle hendelser.</string>
|
||||
<string name="settings_manage_calendar_colors">Velg kalenderfarger</string>
|
||||
<string name="settings_manage_calendar_colors_on">Kalenderfarger behandles av DAVdroid</string>
|
||||
<string name="settings_manage_calendar_colors_off">Kalenderfarger settes ikke av DAVdroid</string>
|
||||
<string name="settings_event_colors">Støtte for fargelegging av hendelser</string>
|
||||
<string name="settings_event_colors_on">Synkroniser hendelsesfarger</string>
|
||||
<string name="settings_event_colors_off">Ikke synkroniser hendelsesfarger</string>
|
||||
<string name="settings_event_colors_off_confirm">Det kan hende at det å skru av hendelsesfarger vil fjerne allerede synkroniserte hendelsesfarger.</string>
|
||||
<!--collection management-->
|
||||
<string name="create_addressbook">Opprett adressebok</string>
|
||||
<string name="create_addressbook_display_name_hint">Min adressebok</string>
|
||||
<string name="create_calendar">Opprett CalDAV-samling</string>
|
||||
<string name="create_calendar_display_name_hint">Min kalender</string>
|
||||
<string name="create_calendar_time_zone">Tidssone</string>
|
||||
<string name="create_calendar_type">Samlingstype:</string>
|
||||
<string name="create_calendar_type_only_events">Kalender (bare hendelser)</string>
|
||||
<string name="create_calendar_type_only_tasks">Gjøremålsliste (bare gjøremål)</string>
|
||||
<string name="create_calendar_type_events_and_tasks">Kombinert (hendelser og gjøremål)</string>
|
||||
<string name="create_collection_color">Velg en samlingsfarge</string>
|
||||
<string name="create_collection_creating">Oppretter samling</string>
|
||||
<string name="create_collection_display_name">Vis navn (tittel) for denne samlingen:</string>
|
||||
<string name="create_collection_display_name_required">Tittel kreves</string>
|
||||
<string name="create_collection_description">Beskrivelse (valgfri):</string>
|
||||
<string name="create_collection_home_set">Hjemmemappe:</string>
|
||||
<string name="create_collection_create">Opprett</string>
|
||||
<string name="delete_collection">Slett samling</string>
|
||||
<string name="delete_collection_confirm_title">Er du sikker?</string>
|
||||
<string name="delete_collection_confirm_warning">Denne samlingen (%s) og all dens data vil bli fjernet fra tjeneren.</string>
|
||||
<string name="delete_collection_deleting_collection">Sletter samling</string>
|
||||
<!--ExceptionInfoFragment-->
|
||||
<string name="exception">En feil har inntruffet</string>
|
||||
<string name="exception_httpexception">En HTTP-feil har inntruffet.</string>
|
||||
<string name="exception_ioexception">En I/O-feil har inntruffet.</string>
|
||||
<string name="exception_show_details">Vis detaljer</string>
|
||||
<!--sync adapters and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Feilrettingsinfo</string>
|
||||
<string name="sync_contacts_read_only_address_book">Adressebok uten skrivetilgang</string>
|
||||
<plurals name="sync_contacts_local_contact_changes_discarded">
|
||||
<item quantity="one">Lokal kontaktendring forkastet</item>
|
||||
<item quantity="other">%d lokale kontaktendringer forkastet</item>
|
||||
</plurals>
|
||||
<string name="sync_error_permissions">DAVdroid-tilganger</string>
|
||||
<string name="sync_error_permissions_text">Ytterligere tilganger kreves</string>
|
||||
<string name="sync_error_calendar">Kalendersynkronisering feilet (%s)</string>
|
||||
<string name="sync_error_contacts">Adresseboksynkronisering mislyktes (%s)</string>
|
||||
<string name="sync_error_tasks">Gjøremålssynkronisering mislyktes (%s)</string>
|
||||
<string name="sync_error">Feil under %s</string>
|
||||
<string name="sync_error_http_dav">Tjener feil under %s</string>
|
||||
<string name="sync_error_local_storage">Databasefeil under %s</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>forbereder synkronisering</item>
|
||||
<item>spør om muligheter</item>
|
||||
<item>behandler lokalt slettede oppføringer</item>
|
||||
<item>forbereder opprettede/endrede oppføringer</item>
|
||||
<item>laster opp opprettede/modifiserte oppføringer</item>
|
||||
<item>sjekker synkroniseringstilstand</item>
|
||||
<item>listefører lokale oppføringer</item>
|
||||
<item>listefører oppføringer annensteds hen</item>
|
||||
<item>sammenlign lokale/oppføringer annensteds hen</item>
|
||||
<item>laster ned oppføringer annensteds fra</item>
|
||||
<item>etterbehandling</item>
|
||||
<item>lagrer synkroniseringstilstand</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">Brukernavn-/passord feil</string>
|
||||
<!--cert4android-->
|
||||
<string name="certificate_notification_connection_security">DAVdroid: Tilkoblingssikkerhet</string>
|
||||
<string name="trust_certificate_unknown_certificate_found">DAVdroid har støtt på et ukjent sertifikat. Har du tiltro til det?</string>
|
||||
</resources>
|
||||
222
app/src/davdroid/res/values-nl/strings.xml
Normal file
222
app/src/davdroid/res/values-nl/strings.xml
Normal file
@@ -0,0 +1,222 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!--common strings-->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="account_title_address_book">DAVdroid Adresboek</string>
|
||||
<string name="address_books_authority_title">Adresboeken</string>
|
||||
<string name="help">Help</string>
|
||||
<string name="manage_accounts">Beheer accounts</string>
|
||||
<string name="please_wait">Een moment geduld...</string>
|
||||
<string name="send">Verzenden</string>
|
||||
<string name="homepage_url">https://davdroid.bitfire.at/?pk_campaign=davdroid-app</string>
|
||||
<!--startup dialogs-->
|
||||
<string name="startup_battery_optimization">Batterij optimalisatie</string>
|
||||
<string name="startup_battery_optimization_message">Android kan mogelijk de DAVdroid synchronisatie stoppen na een paar dagen. Om dit te voorkomen zet u de batterij optimalisatie uit.</string>
|
||||
<string name="startup_battery_optimization_disable">DAVdroid afsluiten</string>
|
||||
<string name="startup_dont_show_again">Niet opnieuw weergeven</string>
|
||||
<string name="startup_donate">Open-Source informatie</string>
|
||||
<string name="startup_donate_message">We zijn blij dat je DAVdroid gebruikt, wat open-source software (GPLv3) is. Omdat de ontwikkeling van DAVdroid hard werk is en duizenden uren in beslag neemt. overweeg alstublieft een donatie.</string>
|
||||
<string name="startup_donate_now">Toon donatie pagina</string>
|
||||
<string name="startup_donate_later">Misschien later</string>
|
||||
<string name="startup_google_play_accounts_removed">Play Store DRM fout-informatie</string>
|
||||
<string name="startup_google_play_accounts_removed_message">Onder bepaalde omstandigheden, kan Play Store DRM ervoor zorgen dat accounts kwijt zijn na een herstart of na een DAVdroid update. Als dit probleem zich bij je voordoet (en alleen dan), Installeer dan \"DAVdroid JB Workaround\" vanuit de Play Store</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Meer informatie</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks niet geinstalleerd</string>
|
||||
<string name="startup_opentasks_not_installed_message">De OpenTasks app is niet beschikbaar, Hierdoor is het voor DAVdroid niet mogelijk om uw taken te synchroniseren.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Na installatie van OpenTasks dient u DAVdroid opnieuw te installeren en de accounts toe te voegen (Android bug).</string>
|
||||
<string name="startup_opentasks_not_installed_install">OpenTasks installeren</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Licentie voorwaarden</string>
|
||||
<string name="about_license_info_no_warranty">Dit programma kom met ABSOLUUT GEEN GARANTIE. Het is gratis software, en je bent welkom dit te herdistribueren onder bepaalde voorwaarden.</string>
|
||||
<!--global settings-->
|
||||
<string name="logging_davdroid_file_logging">DAVDroid bestand loggen</string>
|
||||
<string name="logging_to_external_storage">Loggen naar externe opslag: %s</string>
|
||||
<string name="logging_couldnt_create_file">Kon extern log bestand niet verwijderen: %s</string>
|
||||
<string name="logging_no_external_storage">Externe opslag niet gevonden</string>
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_open">Open navigatie drawer</string>
|
||||
<string name="navigation_drawer_close">Sluit navigatie drawer</string>
|
||||
<string name="navigation_drawer_subtitle">CalDAV/CardDav Sync adapter</string>
|
||||
<string name="navigation_drawer_about">Over / Licentie</string>
|
||||
<string name="navigation_drawer_settings">Instellingen</string>
|
||||
<string name="navigation_drawer_news_updates">Nieuws & updates</string>
|
||||
<string name="navigation_drawer_external_links">Externe links</string>
|
||||
<string name="navigation_drawer_website">Website</string>
|
||||
<string name="navigation_drawer_faq">FAQ</string>
|
||||
<string name="navigation_drawer_faq_url">https://davdroid.bitfire.at/faq/?pk_campaign=davdroid-app</string>
|
||||
<string name="navigation_drawer_donate">Doneren</string>
|
||||
<string name="account_list_empty">Welkom bij DAVdroid!\n\nJe kunt nu een CalDAV/CardDAv account toevoegen.</string>
|
||||
<string name="accounts_global_sync_disabled">Systeembrede automatische synchronisatie is uitgeschakeld</string>
|
||||
<string name="accounts_global_sync_enable">Inschakelen</string>
|
||||
<!--DavService-->
|
||||
<string name="dav_service_refresh_failed">Service herkenning is mislukt</string>
|
||||
<string name="dav_service_refresh_couldnt_refresh">Kon de collectie lijst niet vernieuwen</string>
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">Instellingen</string>
|
||||
<string name="app_settings_user_interface">Gebruikers interface</string>
|
||||
<string name="app_settings_reset_hints">Hints resetten </string>
|
||||
<string name="app_settings_reset_hints_summary">Hints die al gezien zijn opnieuw weergeven</string>
|
||||
<string name="app_settings_reset_hints_success">Alle hints worden opnieuw weergegeven</string>
|
||||
<string name="app_settings_connection">Verbinding</string>
|
||||
<string name="app_settings_override_proxy">Proxy instellingen overschrijven</string>
|
||||
<string name="app_settings_override_proxy_on">Eigen proxy instellingen gebruiken</string>
|
||||
<string name="app_settings_override_proxy_off">Systeem proxy instellingen gebruiken</string>
|
||||
<string name="app_settings_override_proxy_host">HTTP proxy beheerder naam</string>
|
||||
<string name="app_settings_override_proxy_port">HTTP proxy poort</string>
|
||||
<string name="app_settings_security">Beveiliging</string>
|
||||
<string name="app_settings_distrust_system_certs">Systeem certificaten niet vertrouwen</string>
|
||||
<string name="app_settings_distrust_system_certs_on">Systeem en CAs van toegevoegde gebruiker wordt niet vertrouwd</string>
|
||||
<string name="app_settings_distrust_system_certs_off">Systeem en CAs van toegevoegde gebruiker wordt vertrouwd (aanbevolen)</string>
|
||||
<string name="app_settings_reset_certificates">Resetten (niet) vertrouwde certificaten</string>
|
||||
<string name="app_settings_reset_certificates_summary">Resetten alle bewerkte certificaten</string>
|
||||
<string name="app_settings_reset_certificates_success">Alle bewerkte certificaten zijn vrijgemaakt</string>
|
||||
<string name="app_settings_debug">Debuggen</string>
|
||||
<string name="app_settings_log_to_external_storage">Log naar extern bestand</string>
|
||||
<string name="app_settings_log_to_external_storage_on">Loggen naar externe opslag (wanneer beschikbaar)</string>
|
||||
<string name="app_settings_log_to_external_storage_off">Extern bestands loggen uitgeschakeld</string>
|
||||
<string name="app_settings_show_debug_info">Debug info tonen</string>
|
||||
<string name="app_settings_show_debug_info_details">Bekijk/deel software configuratie details</string>
|
||||
<!--AccountActivity-->
|
||||
<string name="account_synchronize_now">Synchroniseer nu</string>
|
||||
<string name="account_synchronizing_now">Aan het synchronizeren...</string>
|
||||
<string name="account_settings">Account instellingen</string>
|
||||
<string name="account_rename">Account hernoemen</string>
|
||||
<string name="account_rename_new_name">Niet opgeslagen lokale informatie mag verloren gaan. Synchronisatie is noodzakelijk na hernoemen. Nieuw account naam:</string>
|
||||
<string name="account_rename_rename">Hernoemen</string>
|
||||
<string name="account_delete">Account verwijderen</string>
|
||||
<string name="account_delete_confirmation_title">Account echt verwijderen?</string>
|
||||
<string name="account_delete_confirmation_text">Alle lokale kopieën van adresboeken, agenda\'s en taken worden verwijderd.</string>
|
||||
<string name="account_refresh_address_book_list">Adresboeken vernieuwen</string>
|
||||
<string name="account_create_new_address_book">Maak een nieuw adresboek</string>
|
||||
<string name="account_refresh_calendar_list">Agenda\'s vernieuwen</string>
|
||||
<string name="account_create_new_calendar">Maak een nieuwe agenda</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">DAVdroid rechten</string>
|
||||
<string name="permissions_calendar">Agenda rechten</string>
|
||||
<string name="permissions_calendar_details">Om CalDAV afspraken te synchroniseren met u agenda dient DAVdroid toegang te verkrijgen. </string>
|
||||
<string name="permissions_calendar_request">Agenda rechten verkrijgen</string>
|
||||
<string name="permissions_contacts">Contact rechten</string>
|
||||
<string name="permissions_contacts_details">Om CalDAV afspraken te synchroniseren met u contacten dient DAVdroid toegang te verkrijgen. </string>
|
||||
<string name="permissions_contacts_request">Contacten rechten verkrijgen</string>
|
||||
<string name="permissions_opentasks">OpenTasks rechten</string>
|
||||
<string name="permissions_opentasks_details">Om CalDAV taken te synchroniseren met uw local takenlijst dient DAVdroid toegang te hebben tot OpenTasks</string>
|
||||
<string name="permissions_opentasks_request">OpenTasks rechten verkrijgen</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_help_url">https://davdroid.bitfire.at/configuration/?pk_campaign=davdroid-app</string>
|
||||
<string name="login_title">Account toevoegen</string>
|
||||
<string name="login_type_email">Inloggen met e-mailadres</string>
|
||||
<string name="login_email_address">Email adres</string>
|
||||
<string name="login_email_address_error">Geldig email adres vereist</string>
|
||||
<string name="login_password">Wachtwoord</string>
|
||||
<string name="login_password_required">Wachtwoord vereist</string>
|
||||
<string name="login_type_url">Inloggen met URL en gebruikersnaam</string>
|
||||
<string name="login_url_must_be_http_or_https">URL moet met http(s):// beginnen</string>
|
||||
<string name="login_url_host_name_required">Hostnaam vereist</string>
|
||||
<string name="login_user_name">Gebruikersnaam</string>
|
||||
<string name="login_user_name_required">Gebruikersnaam vereist</string>
|
||||
<string name="login_base_url">Basis URL</string>
|
||||
<string name="login_login">Login</string>
|
||||
<string name="login_back">Terug</string>
|
||||
<string name="login_create_account">Maak een account</string>
|
||||
<string name="login_account_name">Accountnaam</string>
|
||||
<string name="login_account_name_info">Gebruik je email adres als account naam want Android zal je account naam gebruiken als ORGANIZER veld voor gemaakte afspraken. Je kunt geen 2 accounts met dezelfde naam hebben,</string>
|
||||
<string name="login_account_contact_group_method">Contact groep methode:</string>
|
||||
<string name="login_account_name_required">Accountnaam vereist</string>
|
||||
<string name="login_account_not_created">Account kon niet gemaakt worden.</string>
|
||||
<string name="login_configuration_detection">Configuratie detectie</string>
|
||||
<string name="login_querying_server">Even geduld, verzoek naar server...</string>
|
||||
<string name="login_no_caldav_carddav">Kon geen CalDAV of CardDAV service vinden.</string>
|
||||
<string name="login_view_logs">Bekijk logs</string>
|
||||
<!--AccountSettingsActivity-->
|
||||
<string name="settings_title">Instellingen: %s</string>
|
||||
<string name="settings_authentication">Authenticatie</string>
|
||||
<string name="settings_username">Gebruikersnaam</string>
|
||||
<string name="settings_enter_username">Gebruikersnaam invoeren:</string>
|
||||
<string name="settings_password">Wachtwoord</string>
|
||||
<string name="settings_password_summary">Gebruik het zelfde wachtwoord als op de server.</string>
|
||||
<string name="settings_enter_password">Wachtwoord invoeren:</string>
|
||||
<string name="settings_sync">Synchronisatie</string>
|
||||
<string name="settings_sync_interval_contacts">Contacten verversen</string>
|
||||
<string name="settings_sync_summary_manually">Alleen handmatig</string>
|
||||
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Elke %d minuten + meteen na wijziging</string>
|
||||
<string name="settings_sync_interval_calendars">Agenda\'s verversen</string>
|
||||
<string name="settings_sync_interval_tasks">Taak sync. tussentijd</string>
|
||||
<string name="settings_sync_wifi_only">Sync alleen tijdens WiFi</string>
|
||||
<string name="settings_sync_wifi_only_on">Synchronisatie is voorbehouden tijdens WiFi verbindingen</string>
|
||||
<string name="settings_sync_wifi_only_off">Verbinding type is niet overwogen</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">Contact groep methode</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string-array name="settings_contact_group_method_entries">
|
||||
<item>Groepen zijn apparte VCards</item>
|
||||
<item>Groepen zijn per-contact categories</item>
|
||||
</string-array>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Tijdslimiet verleden afspraken</string>
|
||||
<string name="settings_sync_time_range_past_none">Alle afspraken worden gesynchronizeerd</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="one">Afspraken ouder dan een dag worden genegeerd</item>
|
||||
<item quantity="other">Afspraken ouder dan %d dagen worden genegeerd</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Afspraken ouder dan dit aantal dagen worden genegeerd (mag 0 zijn). Laat leeg om alle afspraken te synchronizeren.</string>
|
||||
<string name="settings_manage_calendar_colors">Agenda kleuren beheren</string>
|
||||
<string name="settings_manage_calendar_colors_on">Agenda kleuren worden door DAVdroid beheerd.</string>
|
||||
<string name="settings_manage_calendar_colors_off">Agenda kleuren worden niet door DAVdroid ingesteld</string>
|
||||
<!--collection management-->
|
||||
<string name="create_addressbook">Maak adresboek</string>
|
||||
<string name="create_addressbook_display_name_hint">Mijn adresboek</string>
|
||||
<string name="create_calendar">Maak CalDAV collectie</string>
|
||||
<string name="create_calendar_display_name_hint">Mijn agenda</string>
|
||||
<string name="create_calendar_time_zone">Tijdzone:</string>
|
||||
<string name="create_calendar_type">Collectie type:</string>
|
||||
<string name="create_calendar_type_only_events">Agenda (alleen afspraken)</string>
|
||||
<string name="create_calendar_type_only_tasks">Takenlijst (alleen taken)</string>
|
||||
<string name="create_calendar_type_events_and_tasks">Gecombineerd (afspraken en taken)</string>
|
||||
<string name="create_collection_color">Stel een collectie kleur in</string>
|
||||
<string name="create_collection_creating">Collectie aan het maken</string>
|
||||
<string name="create_collection_display_name">Weergave naam (titel) van deze collectie:</string>
|
||||
<string name="create_collection_display_name_required">Titel is vereist</string>
|
||||
<string name="create_collection_description">Beschrijving (optioneel):</string>
|
||||
<string name="create_collection_home_set">Begin map:</string>
|
||||
<string name="create_collection_create">Maak</string>
|
||||
<string name="delete_collection">Verwijder collectie</string>
|
||||
<string name="delete_collection_confirm_title">Weet je het zeker?</string>
|
||||
<string name="delete_collection_confirm_warning">Deze collectie (%s) en alle data zal verwijderd worden van de server.</string>
|
||||
<string name="delete_collection_deleting_collection">Collectie aan het verwijderen</string>
|
||||
<!--ExceptionInfoFragment-->
|
||||
<string name="exception">Er is een fout opgetreden.</string>
|
||||
<string name="exception_httpexception">Er is een HTTP fout opgetreden.</string>
|
||||
<string name="exception_ioexception">Er is een I/O fout opgetreden.</string>
|
||||
<string name="exception_show_details">Toon details</string>
|
||||
<!--sync adapters and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Debug informatie</string>
|
||||
<string name="sync_error_permissions">DAVdroid rechten</string>
|
||||
<string name="sync_error_permissions_text">Aanvullende rechten vereist</string>
|
||||
<string name="sync_error_calendar">Agenda synchronisatie is mislukt (%s)</string>
|
||||
<string name="sync_error_contacts">Adresboek synchronisatie is mislukt (%s)</string>
|
||||
<string name="sync_error_tasks">Taak synchronisatie is mislukt (%s)</string>
|
||||
<string name="sync_error">Fout tijdens %s</string>
|
||||
<string name="sync_error_http_dav">Serverfout tijdens %s</string>
|
||||
<string name="sync_error_local_storage">Database fout tijdens %s</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>synchronisatie voorbereiden</item>
|
||||
<item>querying mogelijkheden</item>
|
||||
<item>verwerken van lokaal verwijderde data</item>
|
||||
<item>voorberteiding maken/wijzigen data</item>
|
||||
<item>uploaden maken/bewerken data</item>
|
||||
<item>controleren syngronisatie voortgang</item>
|
||||
<item>lijst lokale data</item>
|
||||
<item>lijst remote data</item>
|
||||
<item>vergelijken lokale/remote data</item>
|
||||
<item>downloaden remote data</item>
|
||||
<item>nabewerking</item>
|
||||
<item>opslaan sync voortgang</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">Gebruikersnaam/wachtwoord onjuist</string>
|
||||
<!--cert4android-->
|
||||
<string name="certificate_notification_connection_security">DAVdroid: Verbinding beveiliging</string>
|
||||
<string name="trust_certificate_unknown_certificate_found">Davdroid is benaderd door een onbekend certificaat. Vertrouwd u dit?</string>
|
||||
</resources>
|
||||
248
app/src/davdroid/res/values-pl/strings.xml
Normal file
248
app/src/davdroid/res/values-pl/strings.xml
Normal file
@@ -0,0 +1,248 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!--common strings-->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="account_title_address_book">Książka adresowa DAVdroid</string>
|
||||
<string name="address_books_authority_title">Książka adresowa</string>
|
||||
<string name="help">Pomoc</string>
|
||||
<string name="manage_accounts">Zadządzaj kontami</string>
|
||||
<string name="please_wait">Proszę czekać</string>
|
||||
<string name="send">Wyślij</string>
|
||||
<!--startup dialogs-->
|
||||
<string name="startup_battery_optimization">Optymalizacja baterii</string>
|
||||
<string name="startup_battery_optimization_message">Android może wyłączyć/zmniejszyć synchronizacje DAVdroid po kilku dniach. Aby temu zapobiec należy wyłączyć optymalizację baterii.</string>
|
||||
<string name="startup_battery_optimization_disable">Wyłącz dla DAVdroid</string>
|
||||
<string name="startup_dont_show_again">Nie pokazuj ponownie</string>
|
||||
<string name="startup_donate">Informacje Open-Source</string>
|
||||
<string name="startup_donate_message">Jesteśmy szczęśliwi, że używasz DAVdroid, który jest oprogramowaniem open-source (GPLv3). Ponieważ rozwijanie DAVdroid jest ciężką pracą i zajęło nam tysiące godzin pracy, prosimy o rozważenie darowizny.</string>
|
||||
<string name="startup_donate_now">Pokaż stronę darowizny</string>
|
||||
<string name="startup_donate_later">Może później</string>
|
||||
<string name="startup_google_play_accounts_removed">Informacje o błędzie DRM Sklepu Play</string>
|
||||
<string name="startup_google_play_accounts_removed_message">Pod pewnymi warunkami, DRM Sklepu Play może powodować, że wszystkie konta DAVdroid mogą zostać usunięte po uruchomieniu lub po uaktualnieniu DAVdroid. Jeśli jesteś dotknięty tym problemem (i tylko wtedy) należy zainstalować \"DAVdroid JB Obejście\" ze Sklepu Play.</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Więcej informacji</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks nie jest zainstalowany</string>
|
||||
<string name="startup_opentasks_not_installed_message">Aplikacja Open Tasks nie jest dostępna, więc DAVdroid nie będzie mógł synchronizować listy zadań. </string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Po zainstalowaniu OpenTasks konieczne jest PRZEINSTALOWANIE DAVdroid i ponowne dodanie twoich kont (błąd Androida).</string>
|
||||
<string name="startup_opentasks_not_installed_install">Zainstaluj OpenTasks</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Warunki licencji</string>
|
||||
<string name="about_license_info_no_warranty">Ten program jest ABSOLUTNIE BEZ GWARANCJI. To jest wolne oprogramowanie i mile widziane jest dalsze rozpowszechnianie go pod pewnymi warunkami.</string>
|
||||
<!--global settings-->
|
||||
<string name="logging_davdroid_file_logging">Plik logów DAVdroid</string>
|
||||
<string name="logging_to_external_storage">Logowanie do zewnątrznej pamięci: %s</string>
|
||||
<string name="logging_couldnt_create_file">Nie można stworzyć zewnętrznego pliku logów: %s</string>
|
||||
<string name="logging_no_external_storage">Zewnętrzna pamięci nie została naleziona</string>
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_open">Otwórz menu nawigacji</string>
|
||||
<string name="navigation_drawer_close">Zamknij menu nawigacji</string>
|
||||
<string name="navigation_drawer_subtitle">Adapter synchronizacji CalDAV/CardDAV</string>
|
||||
<string name="navigation_drawer_about">O DAVdroid / Licencja</string>
|
||||
<string name="navigation_drawer_settings">Ustawienia</string>
|
||||
<string name="navigation_drawer_news_updates">Nowości & aktualizacje</string>
|
||||
<string name="navigation_drawer_external_links">Zewnętrzne odnośniki</string>
|
||||
<string name="navigation_drawer_website">Strona WWW</string>
|
||||
<string name="navigation_drawer_faq">FQA</string>
|
||||
<string name="navigation_drawer_forums">Pomoc / Forum</string>
|
||||
<string name="navigation_drawer_donate">Dotacja</string>
|
||||
<string name="account_list_empty">Witamy w DAVdroid!\n\nMożesz teraz dodać konto CalDAV/CardDAV.</string>
|
||||
<string name="accounts_global_sync_disabled">Automatyczna synchronizacja dla całego systemu jest wyłączona</string>
|
||||
<string name="accounts_global_sync_enable">Włącz</string>
|
||||
<!--DavService-->
|
||||
<string name="dav_service_refresh_failed">Wykrycie serwisu nie powiodło się</string>
|
||||
<string name="dav_service_refresh_couldnt_refresh">Nie można odświeżyć listy kolekcji</string>
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">Ustawienia</string>
|
||||
<string name="app_settings_user_interface">Interfejs użytkownika</string>
|
||||
<string name="app_settings_reset_hints">Zresetuj podpowiedzi</string>
|
||||
<string name="app_settings_reset_hints_summary">Ponownie włącz wskazówki, które zostały usunięte wcześniej</string>
|
||||
<string name="app_settings_reset_hints_success">Wszystkie wskazówki pojawią się ponownie</string>
|
||||
<string name="app_settings_connection">Łączność</string>
|
||||
<string name="app_settings_override_proxy">Nadpisz ustawienia proxy</string>
|
||||
<string name="app_settings_override_proxy_on">Użyj niestandardowych ustawień proxy </string>
|
||||
<string name="app_settings_override_proxy_off">Użyj systemowych ustawień proxy</string>
|
||||
<string name="app_settings_override_proxy_host">Nazwa hosta HTTP proxy</string>
|
||||
<string name="app_settings_override_proxy_port">Port HTTP proxy</string>
|
||||
<string name="app_settings_security">Bezpieczeństwo</string>
|
||||
<string name="app_settings_distrust_system_certs">Usuń certyfikaty systemowe</string>
|
||||
<string name="app_settings_distrust_system_certs_on">CA systemowe i użytkownika nie zostaną dodane</string>
|
||||
<string name="app_settings_distrust_system_certs_off">CA systemowe i użytkownika zostaną dodane (zalecane)</string>
|
||||
<string name="app_settings_reset_certificates">Zresetuj (nie)zaufane certyfikaty</string>
|
||||
<string name="app_settings_reset_certificates_summary">Zresetuj wszystkie niestandardowe certyfikaty.</string>
|
||||
<string name="app_settings_reset_certificates_success">Wszystkie niestandardowe certyfikaty zostały wyczyszczone</string>
|
||||
<string name="app_settings_debug">Debugowanie</string>
|
||||
<string name="app_settings_log_to_external_storage">Loguj to zewnętrznego pliku</string>
|
||||
<string name="app_settings_log_to_external_storage_on">Logowanie do zewnętrznej pamięci (jeśli jest dostępna)</string>
|
||||
<string name="app_settings_log_to_external_storage_off">Logowanie do zewnętrznego pliku jest niedostępne</string>
|
||||
<string name="app_settings_show_debug_info">Pokaż informacje do debug\'owania</string>
|
||||
<string name="app_settings_show_debug_info_details">Przeglądaj/udostępnij oprogramowanie i szczegóły konfiguracji </string>
|
||||
<!--AccountActivity-->
|
||||
<string name="account_synchronize_now">Synchronizuj teraz</string>
|
||||
<string name="account_synchronizing_now">Synchronizcja w toku</string>
|
||||
<string name="account_settings">Ustawienia konta</string>
|
||||
<string name="account_rename">Zmień nazwę konta</string>
|
||||
<string name="account_rename_new_name">Niezapisane dane lokalne mogą zostać usunięte. Ponowna synchronizacja jest wymagana po zmianie nazwy. Nowa nazwa konta:</string>
|
||||
<string name="account_rename_rename">Zmień nazwę</string>
|
||||
<string name="account_delete">Usuń konto</string>
|
||||
<string name="account_delete_confirmation_title">Naprawdę chcesz usunąć konto?</string>
|
||||
<string name="account_delete_confirmation_text">Wszystkie lokalne kopie książek adresowych, kalendarzy i list zadań zostaną usunięte.</string>
|
||||
<string name="account_carddav">CardDAV</string>
|
||||
<string name="account_caldav">CalDAV</string>
|
||||
<string name="account_webcal">Webcal</string>
|
||||
<string name="account_refresh_address_book_list">Odśwież list książek adresowych</string>
|
||||
<string name="account_create_new_address_book">Stwórz nową książkę adresową</string>
|
||||
<string name="account_refresh_calendar_list">Odśwież liste kalendarzy</string>
|
||||
<string name="account_create_new_calendar">Stwórz nowy kalendarz</string>
|
||||
<string name="account_no_webcal_handler_found">Nie znaleziono aplikacji obsługującej Webcal</string>
|
||||
<string name="account_install_icsdroid">Zainstaluj ICSdroid</string>
|
||||
<string name="account_read_only_address_book_selected">Książka adresowa tylko do odczytu - lokalne zmiany zostaną usunięte</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">Uprawnienia DAVdroid</string>
|
||||
<string name="permissions_calendar">Uprawnienia kalendarza</string>
|
||||
<string name="permissions_calendar_details">Aby synchronizować wydarzenia CalDav z lokalnymi kalendarzami, DAVdroid potrzebuje dostępu do twoich kalendarzy.</string>
|
||||
<string name="permissions_calendar_request">Zezwól na uprawnienia kalendarza</string>
|
||||
<string name="permissions_contacts">Uprawnienia kontaktów</string>
|
||||
<string name="permissions_contacts_details">Aby synchronizować książki adresowe CardDAV z lokalnymi kontaktami, DAVdroid potrzebuje dostępu do twoich kontaktów.</string>
|
||||
<string name="permissions_contacts_request">Zezwól na uprawnienia kontaktów</string>
|
||||
<string name="permissions_opentasks">Uprawnienia OpenTasks</string>
|
||||
<string name="permissions_opentasks_details">Aby synchronizować zadania CalDav z lokalnymi listami zadań, DAVdroid potrzebuje dostępu do OpenTasks.</string>
|
||||
<string name="permissions_opentasks_request">Zezwól na uprawnienia OpenTasks</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">Dodaj konto</string>
|
||||
<string name="login_type_email">Logowanie za pomocą adresu e-mail</string>
|
||||
<string name="login_email_address">Adres e-mail</string>
|
||||
<string name="login_email_address_error">Wymanagny poprawny adres e-mail</string>
|
||||
<string name="login_password">Hasło</string>
|
||||
<string name="login_password_required">Wymagane hasło</string>
|
||||
<string name="login_type_url">Logowanie za pomocą adresu URL i nazwy użytkownika</string>
|
||||
<string name="login_url_must_be_http_or_https">URL musi zaczynać się z http(s)://</string>
|
||||
<string name="login_url_host_name_required">Wymagana nazwa hosta</string>
|
||||
<string name="login_user_name">Nazwa użytkownika</string>
|
||||
<string name="login_user_name_required">Wymagana nazwa użtkonika</string>
|
||||
<string name="login_base_url">Podstawowy URL</string>
|
||||
<string name="login_login">Zaloguj</string>
|
||||
<string name="login_back">Wróć</string>
|
||||
<string name="login_create_account">Stwórz konto</string>
|
||||
<string name="login_account_name">Nazwa konta</string>
|
||||
<string name="login_account_name_info">Użyj swojego adresu e-mail jako nazwy konta, ponieważ Android będzie używał nazwy konta jako pola ORGANIZATOR dla wydarzeń, które stworzysz. Nie możesz posiadać dwóch kont o takiej samej nazwie.</string>
|
||||
<string name="login_account_contact_group_method">Metoda grupowania kontaktów:</string>
|
||||
<string name="login_account_name_required">Wymagana nazwa konta</string>
|
||||
<string name="login_account_not_created">Konto nie mogło zostać stworzone</string>
|
||||
<string name="login_configuration_detection">Wykrywanie konfiguracji</string>
|
||||
<string name="login_querying_server">Proszę czekać, odpytywanie serwera...</string>
|
||||
<string name="login_no_caldav_carddav">Nie można znaleźć usługi CalDAV lub CardDAV.</string>
|
||||
<string name="login_view_logs">Pokaż logi</string>
|
||||
<!--AccountSettingsActivity-->
|
||||
<string name="settings_title">Ustawienia: %s</string>
|
||||
<string name="settings_authentication">Uwierzytelnianie</string>
|
||||
<string name="settings_username">Nazwa użytkownika</string>
|
||||
<string name="settings_enter_username">Wpisz nazwe użytkownika:</string>
|
||||
<string name="settings_password">Hasło</string>
|
||||
<string name="settings_password_summary">Zaktualizuj hasło zgodnie z serwerem.</string>
|
||||
<string name="settings_enter_password">Wpisz hasło:</string>
|
||||
<string name="settings_sync">Synchronizacja</string>
|
||||
<string name="settings_sync_interval_contacts">Okres synchronizacji kontktów</string>
|
||||
<string name="settings_sync_summary_manually">Tylko ręcznie</string>
|
||||
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Co %d minut oraz natychmiast przy zmianach lokalnych</string>
|
||||
<string name="settings_sync_interval_calendars">Okres synchronizacji kalendarzy</string>
|
||||
<string name="settings_sync_interval_tasks">Okres synchronizacji list zadań</string>
|
||||
<string-array name="settings_sync_interval_names">
|
||||
<item>Tylko ręcznie</item>
|
||||
<item>Co 15 minut</item>
|
||||
<item>Co 30 minut</item>
|
||||
<item>Co godzinę</item>
|
||||
<item>Co 2 godzinny</item>
|
||||
<item>Co 4 godzinny</item>
|
||||
<item>Raz dziennie</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">Synchronizuj tylko przez WiFi</string>
|
||||
<string name="settings_sync_wifi_only_on">Synchronizacja jest ograniczony do połączeń WiFi</string>
|
||||
<string name="settings_sync_wifi_only_off">Rodzaj połączenia nie jest brany pod uwagę</string>
|
||||
<string name="settings_sync_wifi_only_ssids">Ograniczenia WiFi SSID</string>
|
||||
<string name="settings_sync_wifi_only_ssids_on">Będzie synchronizować tylko w %s</string>
|
||||
<string name="settings_sync_wifi_only_ssids_off">Wszystkie połączenia WiFi będą używane</string>
|
||||
<string name="settings_sync_wifi_only_ssids_message">Nazwy oddzielone przecinkami (SSID) dozwolonych sieci WiFi (pozostaw puste dla wszystkich)</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">Metoda grupowania kontaktów</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Limit czasowy przeszłych wydarzeń</string>
|
||||
<string name="settings_sync_time_range_past_none">Wszystkie wydarzenia zostaną synchronizowane</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="one">Wydarzenia starsze niż jeden dzień zostaną zignorowane.</item>
|
||||
<item quantity="few">Wydarzenia starsze niż %d dni zostaną zignorowane.</item>
|
||||
<item quantity="many">Wydarzenia starsze niż %d dni zostaną zignorowane.</item>
|
||||
<item quantity="other">Wydarzenia starsze niż %d dni zostaną zignorowane.</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Wydarzenia, które są starsze niż podana liczba dni zostaną zignorowane (może być 0). Zostaw puste, aby synchronizować wszystkie wydarzenia.</string>
|
||||
<string name="settings_manage_calendar_colors">Zarządzaj kolorami kalendarza</string>
|
||||
<string name="settings_manage_calendar_colors_on">Kolory kalendarza są zarządzane przez DAVdroid</string>
|
||||
<string name="settings_manage_calendar_colors_off">Kolory kalendarze nie są ustawiane przez DAVdroid</string>
|
||||
<string name="settings_event_colors">Obsługa kolorów wydarzeń</string>
|
||||
<string name="settings_event_colors_on">Synchronizuj kolorów zdarzeń</string>
|
||||
<string name="settings_event_colors_off">Nie synchronizuj kolorów zdarzeń</string>
|
||||
<string name="settings_event_colors_off_confirm">Wyłączenie kolorów zdarzeń może usunąć już zsynchronizowane kolory zdarzeń.</string>
|
||||
<!--collection management-->
|
||||
<string name="create_addressbook">Stwórz książkę adresową</string>
|
||||
<string name="create_addressbook_display_name_hint">Moja książka adresowa</string>
|
||||
<string name="create_calendar">Stwórz kolekcje CalDAV</string>
|
||||
<string name="create_calendar_display_name_hint">Mój kalendarz</string>
|
||||
<string name="create_calendar_time_zone">Strefa czasowa:</string>
|
||||
<string name="create_calendar_type">Typ kolekcji:</string>
|
||||
<string name="create_calendar_type_only_events">Kalendarz (tylko wydarzenia)</string>
|
||||
<string name="create_calendar_type_only_tasks">Lista zadań (tylko zadań)</string>
|
||||
<string name="create_calendar_type_events_and_tasks">Połączone (wydarzenia i zadania)</string>
|
||||
<string name="create_collection_color">Ustaw kolor kolekcji</string>
|
||||
<string name="create_collection_creating">Tworzenie kolekcji</string>
|
||||
<string name="create_collection_display_name">Nazwa wyświetlana (tytuł) kolekcji:</string>
|
||||
<string name="create_collection_display_name_required">Tytuł jest wymagany</string>
|
||||
<string name="create_collection_description">Opis (opcjoalnie)</string>
|
||||
<string name="create_collection_home_set">Ustaw początek:</string>
|
||||
<string name="create_collection_create">Stwórz</string>
|
||||
<string name="delete_collection">Usuń kolekcje</string>
|
||||
<string name="delete_collection_confirm_title">Jesteś pewien?</string>
|
||||
<string name="delete_collection_confirm_warning">Kolekcja (%s) i jej wszystkie dane zostaną usunięte z serwera.</string>
|
||||
<string name="delete_collection_deleting_collection">Usuwanie kolekcji</string>
|
||||
<!--ExceptionInfoFragment-->
|
||||
<string name="exception">Wystąpił błąd.</string>
|
||||
<string name="exception_httpexception">Wystąpił błąd HTTP.</string>
|
||||
<string name="exception_ioexception">Wystąpił błąd I/O.</string>
|
||||
<string name="exception_show_details">Pokaż szczegóły</string>
|
||||
<!--sync adapters and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Informacje debugowe</string>
|
||||
<string name="sync_contacts_read_only_address_book">Książka adresowa tylko do odczytu</string>
|
||||
<plurals name="sync_contacts_local_contact_changes_discarded">
|
||||
<item quantity="one">Lokalny kontakt zostanie odrzucony</item>
|
||||
<item quantity="few">%d lokalne kontakty zostaną odrzucone</item>
|
||||
<item quantity="many">%d lokalne kontakty zostaną odrzucone</item>
|
||||
<item quantity="other">%d lokalne kontakty zostaną odrzucone</item>
|
||||
</plurals>
|
||||
<string name="sync_error_permissions">Uprawnienia DAVdroid</string>
|
||||
<string name="sync_error_permissions_text">Wymagane dodatkowe uprawnienia</string>
|
||||
<string name="sync_error_calendar">Synchronizacja kalendarza nie powiodała się (%s)</string>
|
||||
<string name="sync_error_contacts">Synchronizacja książki adresowej nie powiodała się (%s)</string>
|
||||
<string name="sync_error_tasks">Synchronizacja zadań nie powiodała się (%s)</string>
|
||||
<string name="sync_error">Błąd podczas %s</string>
|
||||
<string name="sync_error_http_dav">Błąd servera podczas %s</string>
|
||||
<string name="sync_error_local_storage">Bład bazy danych podczas %s</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>przygotowanie synchronizacji</item>
|
||||
<item>odpytywanie możliwości</item>
|
||||
<item>przetwarzanie lokalnie usuniętych wpisów</item>
|
||||
<item>przygotowanie stworzonych/zmodyfikowanych wpisów</item>
|
||||
<item>wysyłanie stworzonych/zmodyfikowanych wpisów</item>
|
||||
<item>sprawdzanie stanu synchronizacji</item>
|
||||
<item>listowanie lokalnych wpisów</item>
|
||||
<item>listowanie zdalnych wpisów</item>
|
||||
<item>porównywanie lokalnych/zdalnych wpisów</item>
|
||||
<item>pobieranie zdalnych wpisów</item>
|
||||
<item>przetwarzanie końcowe</item>
|
||||
<item>zapisywanie stanu synchronizacji</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">Błędna nazwa użytkownika lub hasło</string>
|
||||
<!--cert4android-->
|
||||
<string name="certificate_notification_connection_security">DAVdroid: Bezpieczeństwo połączenia</string>
|
||||
<string name="trust_certificate_unknown_certificate_found">DAVdroid napotkał nieznany certyfikat. Czy chcesz go dodać?</string>
|
||||
</resources>
|
||||
249
app/src/davdroid/res/values-pt-rBR/strings.xml
Normal file
249
app/src/davdroid/res/values-pt-rBR/strings.xml
Normal file
@@ -0,0 +1,249 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!--common strings-->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="account_title_address_book">Livro de endereços DAVdroid</string>
|
||||
<string name="address_books_authority_title">Livros de endereços</string>
|
||||
<string name="help">Ajuda</string>
|
||||
<string name="manage_accounts">Gerenciar contas</string>
|
||||
<string name="please_wait">Por favor, aguarde...</string>
|
||||
<string name="send">Enviar</string>
|
||||
<!--startup dialogs-->
|
||||
<string name="startup_battery_optimization">Otimização da bateria</string>
|
||||
<string name="startup_battery_optimization_message">O Android pode desativar/reduzir a sincronização do DAVdroid depois de alguns dias. Para evitar isso, desligue a otimização da bateria.</string>
|
||||
<string name="startup_battery_optimization_disable">Desligar para o DAVdroid</string>
|
||||
<string name="startup_dont_show_again">Não mostrar novamente</string>
|
||||
<string name="startup_donate">Informação sobre Código Aberto</string>
|
||||
<string name="startup_donate_message">Estamos felizes que você usa o DAVdroid, um software de código aberto (GPLv3). O desenvolvimento do DAVdroid é trabalhoso e consome muitas horas de trabalho. Por esse motivo, considere fazer uma doação.</string>
|
||||
<string name="startup_donate_now">Mostrar a página de doações</string>
|
||||
<string name="startup_donate_later">Talvez depois</string>
|
||||
<string name="startup_google_play_accounts_removed">Informação sobre o erro de DRM da Play Store</string>
|
||||
<string name="startup_google_play_accounts_removed_message">Sob certas condições, o DRM da Play Store pode fazer com que todas as contas DAVdroid sejam perdidas depois de uma reinicialização ou atualização do DAVdroid. Se você for afetado por esse problema, instale o \"DAVdroid JB Workaround\" a partir da Play Store.</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Mais informações</string>
|
||||
<string name="startup_opentasks_not_installed">O OpenTasks não está instalado</string>
|
||||
<string name="startup_opentasks_not_installed_message">O aplicativo OpenTasks não está disponível, não sendo possível sincronizar as listas de tarefas pelo DAVdroid.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Depois da instalação do OpenTasks, torna-se necessário REINSTALAR o DAVdroid e adicionar suas contas novamente (erro do Android).</string>
|
||||
<string name="startup_opentasks_not_installed_install">Instalar o OpenTasks</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Termos da Licença</string>
|
||||
<string name="about_license_info_no_warranty">Este programa é distribuído SEM NENHUMA GARANTIA. Ele é software livre e pode ser redistribuído sob algumas condições.</string>
|
||||
<!--global settings-->
|
||||
<string name="logging_davdroid_file_logging">Registro do arquivo do DAVdroid</string>
|
||||
<string name="logging_to_external_storage">Registrando no arquivo externo: %s</string>
|
||||
<string name="logging_couldnt_create_file">Não foi possível criar o arquivo de registro externo: %s</string>
|
||||
<string name="logging_no_external_storage">Armazenamento externo não encontrado</string>
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_open">Abrir a gaveta de navegação</string>
|
||||
<string name="navigation_drawer_close">Fechar gaveta de navegação</string>
|
||||
<string name="navigation_drawer_subtitle">Sincronização de CalDAV/CardDAV</string>
|
||||
<string name="navigation_drawer_about">Sobre / Licença</string>
|
||||
<string name="navigation_drawer_settings">Configurações</string>
|
||||
<string name="navigation_drawer_news_updates">Novidades e atualizações</string>
|
||||
<string name="navigation_drawer_external_links">Links externos</string>
|
||||
<string name="navigation_drawer_website">Site na Web</string>
|
||||
<string name="navigation_drawer_faq">Perguntas fequentes</string>
|
||||
<string name="navigation_drawer_forums">Ajuda / Fóruns</string>
|
||||
<string name="navigation_drawer_donate">Doações</string>
|
||||
<string name="account_list_empty">Bem-vindo ao DAVdroid!\n\nVocê pode adicionar uma conta CalDAV/CardDAV agora.</string>
|
||||
<string name="accounts_global_sync_disabled">A sincronização automática pelo sistema está desativada</string>
|
||||
<string name="accounts_global_sync_enable">Ativar</string>
|
||||
<!--DavService-->
|
||||
<string name="dav_service_refresh_failed">Falha na detecção do serviço</string>
|
||||
<string name="dav_service_refresh_couldnt_refresh">Não foi possível atualizar a lista da coleção</string>
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">Configurações</string>
|
||||
<string name="app_settings_user_interface">Interface de usuário</string>
|
||||
<string name="app_settings_reset_hints">Restaurar sugestões</string>
|
||||
<string name="app_settings_reset_hints_summary">Restaura as sugestões que foram descartadas anteriormente</string>
|
||||
<string name="app_settings_reset_hints_success">Todas as sugestões serão exibidas novamente</string>
|
||||
<string name="app_settings_connection">Conexão</string>
|
||||
<string name="app_settings_override_proxy">Substituir as configurações de proxy</string>
|
||||
<string name="app_settings_override_proxy_on">Usar configurações de proxy personalizadas</string>
|
||||
<string name="app_settings_override_proxy_off">Usar configurações de proxy padrão do sistema</string>
|
||||
<string name="app_settings_override_proxy_host">Nome do servidor proxy HTTP</string>
|
||||
<string name="app_settings_override_proxy_port">Porta do proxy HTTP</string>
|
||||
<string name="app_settings_security">Segurança</string>
|
||||
<string name="app_settings_distrust_system_certs">Desconfiar dos certificados de sistema</string>
|
||||
<string name="app_settings_distrust_system_certs_on">ACs adicionadas pelo usuário e pelo sistema não serão confiáveis</string>
|
||||
<string name="app_settings_distrust_system_certs_off">ACs adicionadas pelo usuário e pelo sistema serão confiáveis (recomendado)</string>
|
||||
<string name="app_settings_reset_certificates">Restaurar certificados não-confiáveis</string>
|
||||
<string name="app_settings_reset_certificates_summary">Restaura a confiança de todos os certificados personalizados</string>
|
||||
<string name="app_settings_reset_certificates_success">Todos os certificados personalizados foram restaurados</string>
|
||||
<string name="app_settings_debug">Depuração</string>
|
||||
<string name="app_settings_log_to_external_storage">Registrar em arquivo externo</string>
|
||||
<string name="app_settings_log_to_external_storage_on">Registrando no armazenamento externo (se disponível)</string>
|
||||
<string name="app_settings_log_to_external_storage_off">O registro em arquivo externo está desativado</string>
|
||||
<string name="app_settings_show_debug_info">Mostrar informações de depuração</string>
|
||||
<string name="app_settings_show_debug_info_details">Exibe/compartilha o software e os detalhes da configuração</string>
|
||||
<!--AccountActivity-->
|
||||
<string name="account_synchronize_now">Sincronizar agora</string>
|
||||
<string name="account_synchronizing_now">Sincronizando</string>
|
||||
<string name="account_settings">Configurações da conta</string>
|
||||
<string name="account_rename">Renomear conta</string>
|
||||
<string name="account_rename_new_name">Dados locais que não foram salvos podem ser descartados. É necessário efetuar uma nova sincronização após renomear. Novo nome da conta:</string>
|
||||
<string name="account_rename_rename">Renomear</string>
|
||||
<string name="account_delete">Excluir conta</string>
|
||||
<string name="account_delete_confirmation_title">Deseja excluir a conta?</string>
|
||||
<string name="account_delete_confirmation_text">Todas as cópias locais dos livros de endereços, calendários e listas de tarefas serão excluídas.</string>
|
||||
<string name="account_carddav">CardDAV</string>
|
||||
<string name="account_caldav">CalDAV</string>
|
||||
<string name="account_webcal">Webcal</string>
|
||||
<string name="account_refresh_address_book_list">Atualizar lista de livros de endereços</string>
|
||||
<string name="account_create_new_address_book">Criar novo livro de endereços</string>
|
||||
<string name="account_refresh_calendar_list">Atualizar lista de calendários</string>
|
||||
<string name="account_create_new_calendar">Criar novo calendário</string>
|
||||
<string name="account_no_webcal_handler_found">Não foi encontrado um aplicativo capaz de lidar com Webcal</string>
|
||||
<string name="account_install_icsdroid">Instalar ICSdroid</string>
|
||||
<string name="account_read_only_address_book_selected">Livro de endereços somente leitura – as alterações locais serão descartadas!</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">Permissões do DAVdroid</string>
|
||||
<string name="permissions_calendar">Permissões do calendário</string>
|
||||
<string name="permissions_calendar_details">Para sincronizar os eventos CalDAV com seus calendários locais, o DAVdroid precisa acessar seus calendários.</string>
|
||||
<string name="permissions_calendar_request">Solicitar permissão do calendário</string>
|
||||
<string name="permissions_contacts">Permissões dos contados</string>
|
||||
<string name="permissions_contacts_details">Para sincronizar livros de endereços CardDAV com seus contatos locais, o DAVdroid precisa acessar seus contatos.</string>
|
||||
<string name="permissions_contacts_request">Solicitar permissão dos contatos</string>
|
||||
<string name="permissions_opentasks">Permissões do OpenTasks</string>
|
||||
<string name="permissions_opentasks_details">Para sincronizar tarefas CalDAV com suas listas de tarefas locais, o DAVdroid precisa acessar o OpenTasks.</string>
|
||||
<string name="permissions_opentasks_request">Solicitar permissão do OpenTasks</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_help_url">https://davdroid.bitfire.at/configuration/?pk_campaign=davdroid-app</string>
|
||||
<string name="login_title">Adicionar conta</string>
|
||||
<string name="login_type_email">Autenticação com endereço de e-mail</string>
|
||||
<string name="login_email_address">Endereço de e-mail</string>
|
||||
<string name="login_email_address_error">É necessário um e-mail válido</string>
|
||||
<string name="login_password">Senha</string>
|
||||
<string name="login_password_required">É necessário uma senha</string>
|
||||
<string name="login_type_url">Autenticação com usuário e URL</string>
|
||||
<string name="login_url_must_be_http_or_https">A URL deve começar com http(s)://</string>
|
||||
<string name="login_url_host_name_required">É necessário um nome de máquina</string>
|
||||
<string name="login_user_name">Usuário</string>
|
||||
<string name="login_user_name_required">É necessário um nome de usuário</string>
|
||||
<string name="login_base_url">URL base</string>
|
||||
<string name="login_login">Autenticar</string>
|
||||
<string name="login_back">Voltar</string>
|
||||
<string name="login_create_account">Criar conta</string>
|
||||
<string name="login_account_name">Nome da conta</string>
|
||||
<string name="login_account_name_info">Use seu endereço de e-mail como nome da conta porque o Android irá usar esse nome como campo AGENDA nos eventos que você criar. Não é possível ter duas contas com o mesmo nome.</string>
|
||||
<string name="login_account_contact_group_method">Método do grupo Contato:</string>
|
||||
<string name="login_account_name_required">É necessário um nome de conta</string>
|
||||
<string name="login_account_not_created">A conta não pôde ser criada</string>
|
||||
<string name="login_configuration_detection">Detecção de configuração</string>
|
||||
<string name="login_querying_server">Aguarde, procurando servidor...</string>
|
||||
<string name="login_no_caldav_carddav">Não foi possível encontrar o serviço CalDAV ou CardDAV.</string>
|
||||
<string name="login_view_logs">Exibir registros</string>
|
||||
<!--AccountSettingsActivity-->
|
||||
<string name="settings_title">Configurações: %s</string>
|
||||
<string name="settings_authentication">Autenticação</string>
|
||||
<string name="settings_username">Nome do usuário</string>
|
||||
<string name="settings_enter_username">Digite o nome do usuário:</string>
|
||||
<string name="settings_password">Senha</string>
|
||||
<string name="settings_password_summary">Atualize a senha de acordo com seu servidor</string>
|
||||
<string name="settings_enter_password">Digite sua senha:</string>
|
||||
<string name="settings_sync">Sincronização</string>
|
||||
<string name="settings_sync_interval_contacts">Intervalo sinc. de contatos</string>
|
||||
<string name="settings_sync_summary_manually">Apenas manualmente</string>
|
||||
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">A cada %d minutos + imediatamente nas alterações locais</string>
|
||||
<string name="settings_sync_interval_calendars">Intervalo sinc. de calendários</string>
|
||||
<string name="settings_sync_interval_tasks">Intervalo sinc. de tarefas</string>
|
||||
<string-array name="settings_sync_interval_names">
|
||||
<item>Apenas manualmente</item>
|
||||
<item>A cada 15 minutos</item>
|
||||
<item>A cada 30 minutos</item>
|
||||
<item>A cada hora</item>
|
||||
<item>A cada 2 horas</item>
|
||||
<item>A cada 4 horas</item>
|
||||
<item>Uma vez por dia</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">Sincronizar apenas por Wi-Fi</string>
|
||||
<string name="settings_sync_wifi_only_on">Sincronização restrita a conexões Wi-Fi</string>
|
||||
<string name="settings_sync_wifi_only_off">O tipo de conexão não é considerado</string>
|
||||
<string name="settings_sync_wifi_only_ssids">Restrição de WiFi SSID</string>
|
||||
<string name="settings_sync_wifi_only_ssids_on">Sincronizar apenas com %s</string>
|
||||
<string name="settings_sync_wifi_only_ssids_off">Todas as conexões WiFi serão usadas</string>
|
||||
<string name="settings_sync_wifi_only_ssids_message">Nomes separados por vírgula (SSIDs) das redes WiFi (deixe em branco para todas)</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">Método do grupo Contato</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string-array name="settings_contact_group_method_entries">
|
||||
<item>Grupos são VCards separados</item>
|
||||
<item>Grupos são categorias por contato</item>
|
||||
</string-array>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Limite de tempo para eventos passados</string>
|
||||
<string name="settings_sync_time_range_past_none">Todos os eventos serão sincronizados</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="one">Os eventos que ocorreram a mais de um dia serão ignorados</item>
|
||||
<item quantity="other">Eventos que ocorreram a mais de %d dias serão ignorados</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Os eventos que ocorreram antes desse número de dias serão ignorados (pode ser 0). Deixe em branco para sincronizar todos os eventos.</string>
|
||||
<string name="settings_manage_calendar_colors">Gerenciar cores dos calendários</string>
|
||||
<string name="settings_manage_calendar_colors_on">Cores dos calendários definidas pelo DAVdroid</string>
|
||||
<string name="settings_manage_calendar_colors_off">Cores dos calendários não definidas pelo DAVdroid</string>
|
||||
<string name="settings_event_colors">Suporte para cor de evento</string>
|
||||
<string name="settings_event_colors_on">Sincronizar cores de eventos</string>
|
||||
<string name="settings_event_colors_off">Não sincronizar cores de eventos</string>
|
||||
<string name="settings_event_colors_off_confirm">Desativar as cores de eventos poderá remover as que já foram sincronizadas</string>
|
||||
<!--collection management-->
|
||||
<string name="create_addressbook">Criar livro de endereços</string>
|
||||
<string name="create_addressbook_display_name_hint">Meu livro de endereços</string>
|
||||
<string name="create_calendar">Criar coleção CalDAV</string>
|
||||
<string name="create_calendar_display_name_hint">Meu calendário</string>
|
||||
<string name="create_calendar_time_zone">Fuso horário:</string>
|
||||
<string name="create_calendar_type">Tipo de coleção:</string>
|
||||
<string name="create_calendar_type_only_events">Calendário (apenas eventos)</string>
|
||||
<string name="create_calendar_type_only_tasks">Lista de tarefas (apenas tarefas)</string>
|
||||
<string name="create_calendar_type_events_and_tasks">Combinado (eventos e tarefas)</string>
|
||||
<string name="create_collection_color">Definir a cor da coleção</string>
|
||||
<string name="create_collection_creating">Criando a coleção</string>
|
||||
<string name="create_collection_display_name">Mostrar o nome (título) desta coleção:</string>
|
||||
<string name="create_collection_display_name_required">É necessário um título</string>
|
||||
<string name="create_collection_description">Descrição (opcional):</string>
|
||||
<string name="create_collection_home_set">Indique o endereço:</string>
|
||||
<string name="create_collection_create">Criar</string>
|
||||
<string name="delete_collection">Excluir coleção</string>
|
||||
<string name="delete_collection_confirm_title">Tem certeza?</string>
|
||||
<string name="delete_collection_confirm_warning">Esta coleção (%s) e todos os seus dados serão removidos do servidor.</string>
|
||||
<string name="delete_collection_deleting_collection">Excluindo coleção</string>
|
||||
<!--ExceptionInfoFragment-->
|
||||
<string name="exception">Ocorreu um erro.</string>
|
||||
<string name="exception_httpexception">Ocorreu um erro de HTTP.</string>
|
||||
<string name="exception_ioexception">Ocorreu um erro de leitura/gravação.</string>
|
||||
<string name="exception_show_details">Mostrar detalhes</string>
|
||||
<!--sync adapters and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Informações de depuração</string>
|
||||
<string name="sync_contacts_read_only_address_book">Livro de endereços somente leitura</string>
|
||||
<plurals name="sync_contacts_local_contact_changes_discarded">
|
||||
<item quantity="one">Alteração local de contato descartada</item>
|
||||
<item quantity="other">%d alterações locais de contatos descartadas</item>
|
||||
</plurals>
|
||||
<string name="sync_error_permissions">Permissões do DAVdroid</string>
|
||||
<string name="sync_error_permissions_text">É necessário permissões adicionais</string>
|
||||
<string name="sync_error_calendar">Falha na sincronização do calendário (%s)</string>
|
||||
<string name="sync_error_contacts">Falha na sincronização do livro de endereços (%s)</string>
|
||||
<string name="sync_error_tasks">Falha na sincronização das tarefas (%s)</string>
|
||||
<string name="sync_error">Erro ao %s</string>
|
||||
<string name="sync_error_http_dav">Erro do servidor ao %s</string>
|
||||
<string name="sync_error_local_storage">Erro do banco de dados ao %s</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>preparando sincronização</item>
|
||||
<item>procurando habilidades</item>
|
||||
<item>processando os itens excluídos localmente</item>
|
||||
<item>preparando os itens criados/modificados</item>
|
||||
<item>enviando os itens criados/modificados</item>
|
||||
<item>verificando o estado da sincronização</item>
|
||||
<item>listando os itens locais</item>
|
||||
<item>listando os itens remotos</item>
|
||||
<item>comparando os itens locais/remotos</item>
|
||||
<item>baixando os itens remotos</item>
|
||||
<item>pós-processamento</item>
|
||||
<item>salvando o estado da sincronização</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">Usuário/senha incorreto</string>
|
||||
<!--cert4android-->
|
||||
<string name="certificate_notification_connection_security">DAVdroid: Segurança da conexão</string>
|
||||
<string name="trust_certificate_unknown_certificate_found">O DAVdroid encontrou um certificado desconhecido. Deseja torná-lo confiável?</string>
|
||||
</resources>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user