Compare commits

..

2148 Commits

Author SHA1 Message Date
Arnau Mora
46e8c4522b Updated name, package and color
Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-10-25 17:47:23 +02:00
Sunik Kupfer
d00353ba9c Replace android sync framework result class with our own (#1094)
* Use our own SyncResult data class

* Minor comment changes

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-10-25 14:13:43 +02:00
Ricki Hirner
dc0d4f371a More compatible processing of multiget responses (#1099)
* Ignore multi-get responses without calendar/contact data

* Add comment
2024-10-25 12:44:54 +02:00
Arnau Mora
3d198f5454 LocalAddressBook: rename account to addressbookAccount (#1095)
* Upgraded vcard4android

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Replaced all usages of addressBookAccount

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Minor changes

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-10-22 14:07:53 +02:00
Ricki Hirner
1802740a2d Version bump to 4.4.3.2 2024-10-20 16:32:12 +02:00
Ricki Hirner
138e517d23 LocalAddressBook: move contacts when renaming the address book account (#1084)
* LocalAddressBook: move contacts when renaming the address book account

* Don't make contacts dirty when moving

* Move isDirty to tests because it's only required for tests

* We don't have to set the user-data twice

* Add test for groups
2024-10-20 16:31:31 +02:00
Ricki Hirner
166b2ac220 Bump version to 4.4.3.1 2024-10-18 16:53:28 +02:00
Ricki Hirner
450a418994 Don't crash when logging null parameter (#1081) 2024-10-18 16:51:26 +02:00
Ricki Hirner
d4e9e2a8f7 Reduce warnings, lint 2024-10-17 16:40:09 +02:00
Ricki Hirner
ecc59dda99 Update dependencies 2024-10-17 16:32:35 +02:00
Ricki Hirner
9c2afbab09 Fetch translations from Transifex 2024-10-17 16:14:04 +02:00
Ricki Hirner
cebf2d9dfd Version bump to 4.4.3 2024-10-17 16:10:18 +02:00
Sunik Kupfer
5f49c675c8 Try to adhere to google play guidelines for background location permission (bitfireAT/davx5#614)
* Always show "WiFi SSID card" in account settings when SSID restriction is active and adapt content according to whether all conditions are met or not

* Move explanation to top and add paragraph

* Remove unnecessary parenthesis

* Use two text composables and no spacer

* Fix preview
2024-10-16 11:11:44 +02:00
Ricki Hirner
62c46e123d Avoid very long log lines and resulting OOM (#1073)
* PlainTextFormatter: truncate log lines to ~10000 characters

* Update vcard4android which doesn't dump Contact photos anymore

* Add truncation test
2024-10-15 15:22:03 +02:00
Ricki Hirner
5f1215801d Update dependencies 2024-10-15 12:32:45 +02:00
Ricki Hirner
930977c44b Make collections in CollectionsList clickable again (#1075)
CollectionsList: use modifier again
2024-10-15 11:40:09 +02:00
Arnau Mora
a0d152a66f Fixed surface container color in dark theme (#1069)
* Fixed surface color in dark theme

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Account screen: higher contrast for collection cards

* Account screen: use normal instead of elevated cards

* Adapt colors of card lists

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-10-14 15:03:08 +02:00
Ricki Hirner
a8883427bc Fetch translations from Transifex 2024-10-10 18:25:36 +02:00
Ricki Hirner
7a8dbef80b Version bump to 4.4.3-rc.1 2024-10-10 18:23:05 +02:00
Ricki Hirner
4a40bb3d6f Syncer: make sure collections which are deleted are not synced (#1065)
* Syncer: make sure collections which are deleted are not synced

* Syncer: log when local collection is removed

* Update KDoc and tests

* Handle all CRUD work in updateCollections

* Update naming, KDoc, tests

* Minor changes (KDoc, naming)
2024-10-10 18:08:13 +02:00
Arnau Mora
c805e549ff [Push] Show notification on push notification (until sync is started) (#1043)
* Added sync pending notification

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Moved notify function to PushNotificationManager

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added ongoing and only-alert-once

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added notification hiding

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Got rid of `cancel`

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed comments

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added content intent and sub text

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Updated usages

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Review changes

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-10-10 15:50:53 +02:00
Sunik Kupfer
26a670c181 Fix old address book accounts not being deleted (#1039)
* Log warning instead of throwing exception when not possible to find account for address book account

* Run sync and accounts cleanup in migration

* Rename accounts in migration

* Run account settings migrations on background thread

* Revert "Run account settings migrations on background thread"

This reverts commit 6b578da4f1.

* Add tests for AccountsCleanupWorker

* Move companion object to end of class

* Don't use AccountRepository for address book accounts

* Update account user data in LocalAddressBook

* Minor changes (naming etc)

* Add log line when migrating

* Try to fix test error

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-10-09 13:28:54 +02:00
Arnau Mora
5b54c9dff0 MKCALENDAR: send VTIMEZONE in calendar-timezone (#1044)
* Upgrade dav4jvm

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* separate CalendarTimezone and CalendarTimezoneId

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed timezone name setting

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed `VTIMEZONE` conversion

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Using text instead of CDATA

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed spec

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added comment

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Renamed `timezoneDef` to `timezoneId`

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Upgrade dav4jvm

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* separate CalendarTimezone and CalendarTimezoneId

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed timezone name setting

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed `VTIMEZONE` conversion

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Using text instead of CDATA

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed spec

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added comment

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Renamed `timezoneDef` to `timezoneId`

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* [CI] Update workflows to Java 21

* Set default value of the timezone state to null

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-10-09 00:12:34 +02:00
Ricki Hirner
1ca73b67a4 Add KDoc, rename AccountUtils to SystemAccountUtils (#1059) 2024-10-07 18:52:18 +02:00
Ricki Hirner
194c587476 Update Compose, Kotlin, gradle 2024-10-07 12:38:18 +02:00
Sunik Kupfer
1193027e5f Add account name to address book account name (#1050)
* Add account name to address book account name

* Add parenthesis for account name and a hashtag for the collection id

* Use the correct id

* Move DI entry point to where it is used
2024-10-07 12:26:53 +02:00
Ricki Hirner
7de7980860 Use SafeAndroidUriHandler to prevent crashes when no browser is installed (#1058)
* Use SafeAndroidUriHandler in AppTheme

* UiUtils: use DI for Logger

* SyncWorkerManager: use DI for Logger
2024-10-07 10:44:36 +02:00
Ricki Hirner
fc7f42c6fa Sync worker management: move logic out of companion object (#1056)
* Sync worker management: move logic from companion object to new class

* Fix tests

* Move re-sync inputs from [OneTimeSyncWorker] to [BaseSyncWorker] as they're processed there

* Remove useless Companion
2024-10-07 09:34:02 +02:00
Sunik Kupfer
196bfb3aea Don't use AccountSettings on main thread (#1049)
* Document that AccountSettings shouldn't be used in the main thread

* Throw exception when AccountSettings are used on the main thread

* Don't access AccountSettings on main thread

* Don't access AccountSettings on main thread

* Don't access AccountSettings on main thread
2024-10-03 15:36:42 +02:00
Sunik Kupfer
cb5798833d Ignore lint AppLinkUrlError (#1053) 2024-10-03 15:33:39 +02:00
Ricki Hirner
a1148613e9 [CI] Update workflows to Java 21 2024-10-02 13:08:11 +02:00
Ricki Hirner
12529fa9bd Update toolchain and AGP 2024-10-02 12:54:50 +02:00
Ricki Hirner
d743d19a3d AccountsScreen: better preview 2024-09-19 15:42:45 +02:00
Ricki Hirner
4dcee27e22 Version bump to 4.4.3-beta.1 2024-09-19 15:17:20 +02:00
Sunik Kupfer
b6ceaa7efc Remove concept of main accounts (#989)
* Acquire account settings via address book account

* Extract the code to find an address books main account to the account repository

* Use collection id as reference in address book account

* Remove obsolete baos

* Find main account directly from collection in SyncManager

* Require main account to get account settings

* Stop deleting address book accounts without a main account, since they may exist on their own now

* Require content provider and introduce static deleteByCollection method

* Update KDoc

* Show all address book accounts separately

* Drop mainAccount method

* [DI] Use AssistedInject for LocalAddressBook

* Renaming, remove "main account" concept

* Fix debug info

* AccountsCleanupWorker: Rename main account to account

* Further remove main accounts

* Reduce redundancy

* AccountSettings: check account type

* AccountSettingsMigrations: drop v5 -> v6 migration (not tested anyway)

* AccountRepository: directly delete accounts

* Remove obsolete workerAccount

* Get all address books, even if not sync enabled

* Delete orphan address book accounts

* Rename two more occurrences of main account concept

* AccountSettings: allow test accounts

* Syncer: rename methods for clarity, add KDoc

* Drop empty test class

* Make code more readable and add comment

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-09-19 15:06:45 +02:00
Ricki Hirner
5c6f712d32 Update AGP and dependencies 2024-09-19 14:02:34 +02:00
Arnau Mora
5180b99af2 Moved pull-to-refresh indicator below tabs (#1028)
Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-09-16 15:48:48 +02:00
Arnau Mora
dcb7e315b9 ClickableText for URLs has been deprecated (#1024)
* Got rid of `UrlAnnotation`

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Deprecated and suggested a ReplaceWith

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Optimized imports

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Replaced usages of `ClickableTextWithLink`

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed `ClickableTextWithLink`

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Migrated `ClickableTextWithLink`

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Remove experimental text api annotations

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Sunik Kupfer <kupfer@bitfire.at>
2024-09-16 15:02:17 +02:00
Arnau Mora
111481cd00 Added isLoading to Assistant (#1027)
Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-09-16 14:35:36 +02:00
Ricki Hirner
4dc7df7c53 Reword login text (#1026)
- Change empty accounts text
- Add privacy note to Login screen
- Changed last button text
2024-09-16 14:34:08 +02:00
Arnau Mora
cf609288e1 Update Dependencies (#1017)
* Upgrade dependencies

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Upgrade dependencies

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Migrated pull to refresh

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Migrated `LocalMinimumInteractiveComponentEnforcement`

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed disabling of linting

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Optimize imports

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Increased indicator show time

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-09-14 13:22:43 +02:00
Ricki Hirner
0b9d4cd3b3 Unsubscribe push from unsynced collections (#1011)
* Unsubscribe push from unsynced collections

* Remove subscription from DB, too

* Subscription: catch HTTP errors
2024-09-10 12:07:04 +02:00
Arnau Mora
0581417bba Increase SDK level to 35 (#1003)
* Increase SDK level

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed nullability issues

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Increase SDK level

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed nullability issues

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Replaced `removeFirst()` by `removeAt(0)`

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Switched to null check instead of NPE catch

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Using orEmpty

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-09-10 11:18:40 +02:00
Ricki Hirner
f8fb016a27 [CI] Better test names 2024-09-06 11:29:15 +02:00
Ricki Hirner
8c3d1cdeae InitCalendarProviderRule: make multiple attempts to create a calendar (#1007) 2024-09-06 11:18:08 +02:00
Arnau Mora
4a4dc24cdf dark theme / black text basically unreadable on dark background (#986)
* Provided content color

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Adjusted bar color

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Got rid of theme changes

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added wrapping scaffold

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Changed colors

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-09-06 10:07:30 +02:00
Arnau Mora
49a51ef384 Reproducible Builds (for IzzyOnDroid) (#995)
* Added `BUILD_DATE` environment variable

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Excluded `generated`

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed argument

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Using build time from git

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed unused import

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Got rid of build date

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Got rid of build date

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2024-09-06 09:48:29 +02:00
Arnau Mora
fc698040aa lint fails in AboutActivity (#1001)
* Disabled `CoroutineCreationDuringComposition` in lint

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Moved disable

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-09-04 17:46:44 +02:00
Ricki Hirner
6cbd71ab50 Update dependencies (including AGP) 2024-09-04 11:03:29 +02:00
Ricki Hirner
47f078dcd7 Version bump to 4.4.3-alpha.1 2024-08-21 12:23:54 +02:00
Ricki Hirner
be6ab8728c Update dependencies and Gradle 2024-08-21 12:18:51 +02:00
Ricki Hirner
2908bba298 [CI] Compile job: only compile sources, not tests 2024-08-21 12:01:22 +02:00
Sunik Kupfer
b962b68631 Use standard content provider instead of TaskProvider in TaskSyncer (#982)
* Update ical4android

* Use standard content provider in TaskSyncer

* Check version instead of acquiring TaskProvider

* Add sync result error

---------

Co-authored-by: Arnau Mora <arnyminerz@proton.me>
2024-08-20 10:47:50 +02:00
Sunik Kupfer
fca7c09105 Tests for sync algorithm (#974)
* Prepare Syncer for tests

* Test sync honors preparation result

* Refactor sync algorithm into smaller testable methods

* Write weak tests for the individual methods

* Update KDoc

* Minor changes
 - update comments
 - update test method spacing
 - replace empty array method

---------

Co-authored-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-08-19 13:42:22 +02:00
Sunik Kupfer
60c6aba2d2 Log stop reason when sync worker is cancelled (#984) 2024-08-17 16:06:21 +02:00
Ricki Hirner
70f6f2603e SyncAdapterServices: Use a coroutine scope to cancel waiting on framework request (#977)
* SyncAdapterServices: Use a coroutine scope to cancel waiting on framework request

* Added tests
2024-08-14 14:00:54 +02:00
Ricki Hirner
5d4c9c8d94 Don't overwrite calendar/task list color with default color (#971)
Calendars, task lists: always set color at creation, then overwrite only when available from server
2024-08-14 10:13:08 +02:00
Ricki Hirner
4378bee042 [CI] Skip compile job when not on main branch (#978) 2024-08-13 13:19:17 +02:00
Ricki Hirner
3776b50bbc Update dependencies, Kotlin, AGP 2024-08-12 14:19:00 +02:00
Sunik Kupfer
a9c7e1929f Fix sync not running directly after enabling a collection (#966)
Retrieve local sync collections only once and return them on creation
2024-08-12 11:55:35 +02:00
Ricki Hirner
318b9be77e AccountRepository: don't add onAccountsUpdated listener on main thread 2024-08-08 11:30:56 +02:00
Ricki Hirner
26cb845950 Reduce StrictMode annoyance 2024-08-08 10:40:25 +02:00
Sunik Kupfer
eae6d0c578 Fix coincidence naming of LocalCollection members (#957)
* Fix overlapping method name and use interface everywhere

* Fix overlapping property name

* Update logger usage

---------

Co-authored-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-08-08 10:18:21 +02:00
Ricki Hirner
6df0925e50 Version bump to 4.4.2 2024-08-06 13:31:14 +02:00
Ricki Hirner
f7ee1ea931 [UI] Show push support status in collection details (#961)
* Collection details: show WebPush support

* Collection view: show Push subscription time
2024-08-06 13:29:31 +02:00
Ricki Hirner
16731d3a5a Suppress dnsjava warning because of missing Context (#959) 2024-08-06 12:39:57 +02:00
Ricki Hirner
54e09acca3 Don't generate/let Gradle user cache grow in main branch 2024-08-06 11:26:46 +02:00
Ricki Hirner
46698a76b5 Fetch translations from Transifex 2024-08-05 17:41:06 +02:00
Ricki Hirner
e26a8519ff Version bump to 4.4.2-rc.1 2024-08-05 17:40:06 +02:00
Arnau Mora
410c70a47d Passing collection directly (#927)
* Passing `collection` directly

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed imports

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Fix tests

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Log sync time using repository; no need for account/service check anymore

* SyncManagerTest: don't write SyncStats to DB

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-08-05 17:24:46 +02:00
Sunik Kupfer
bf1bdfc8ab Generalize syncer (#907)
* Use address book contacts content provider provided by syncer

* Close any acquired content provider after sync

* Acquire/Check task provider provider as preparation step

* Provide sync arguments at syncer creation

* Acquire ContentProviderClient in syncer implementations

* Generalize sync algorithm in Syncer

* Use contacts authority for address books

* Generalize sync algorithm in CalendarSyncer

* Generalize sync algorithm in SyncerTest

* Generalize sync algorithm in TaskSyncer

* Rename preparation method and add an after sync method

* Generalize sync algorithm in JtxSyncer

* Generalize sync algorithm in AddressBookSyncer

* Use repositories instead of DAOs

* Replace deprecated log statements

* Use generic type for collection types

* Infer authorities when possible and pass only task authorities along

* No need to close TaskProvider explicitly

* Use colors only where needed

* Use provider with auto closable

* Get sync collections in syncer implementations

* Delete syncer test

* Pass provider through methods instead of using lateinit property

* Reorder constructor arguments

* Remove trailing commas

* Remove obsolete undocumented conditional

* Reorder methods

* Reorder methods

* Abort sync when preparations fail

* Drop obsolete permission check

* Use generics for url and delete

* Use generic for update

* Use generic for syncCollection

* Rename create to createCollection for consistency

* Revert "Rename create to createCollection for consistency"

This reverts commit 0fee4fe7fcf3ec8ef965c9a2e0db991a1bbbbcf7.

* Revert "Use generic for syncCollection"

This reverts commit ae129fd17e06146a1e9f8631e3cdbd2dc0a4db06.

* Revert "Use generic for update"

This reverts commit 42dd665851ba83a75bb498b98bd56624e2b09647.

* Revert "Use generics for url and delete"

This reverts commit 7ae1425039656d4a9937628ca1799ce8c59ceebb.

* Move delete() to LocalCollection

* Move url to LocalCollection

* Fix local test collection

* Minor changes

* Minor changes
- make sync private
- use to autoclose provider

* Query for sync collections once only

* Add KDoc and update comments

* Make property a local variable

* Update KDoc

* Add back ose conditional

* Remove blank line at beginning of method

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-08-05 17:22:07 +02:00
Arnau Mora
5cbbfb39aa Update dav4jvm for better logging (#956)
* Upgrade dav4jvm

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Using `XmlReader`

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-08-05 16:42:24 +02:00
Ricki Hirner
62d5a21d05 Update ical4android, specify ical4j log level (#946)
* Update ical4android, specify ical4j log level (was previously in ical4android)

* Add "dependencies" label for auto-generated release notes
2024-07-28 16:54:01 +02:00
Ricki Hirner
2a7cf1ae17 Fix R8 rules 2024-07-28 14:34:55 +02:00
Ricki Hirner
1062eaa58a Version bump to 4.4.2-beta.1 2024-07-28 13:54:03 +02:00
Ricki Hirner
69bde87589 WebDAV mounts: fix quota view (#945)
* Refresh quota for newly created mounts

* WebDAV mounts screen: take quota from root document
2024-07-28 13:53:39 +02:00
Ricki Hirner
4111fe08d2 Drop userAgent build configuration variable 2024-07-28 12:36:59 +02:00
Ricki Hirner
19fb969040 Drop userAgent build configuration variable 2024-07-28 11:20:25 +02:00
Ricki Hirner
74a22cd24d Update dependencies 2024-07-28 11:14:53 +02:00
Ricki Hirner
4e264076d1 Amend PR template 2024-07-26 14:50:21 +02:00
Ricki Hirner
c3fe1b04e5 Backport minor changes from non-ose (#941)
Login: correctly combine input flows for account details UI state (backport bitfireAT/davx5#575)
2024-07-26 12:04:19 +02:00
Ricki Hirner
342314363b Simplify InitCalendarProviderRule (#940)
Simplify InitCalendarProviderRule
2024-07-25 22:56:07 +02:00
Ricki Hirner
d0358a9980 Use test account type (without sync adapters) for instrumented tests (bitfireAT/davx5#601)
* Use test account type (without sync adapters as side effects) for instrumented tests

* Provide standardized way to get test account; re-enable LocalAddressBookTest
2024-07-25 20:57:23 +02:00
Ricki Hirner
fbcf6996ad Don't access DB in UnifiedPushReceiver main thread 2024-07-24 17:43:58 +02:00
Ricki Hirner
cf994ee82e Move UrlUtils methods to DavUtils 2024-07-24 17:15:57 +02:00
Ricki Hirner
9880dd5158 Move TaskUtils to TasksAppManager (closes #937) 2024-07-24 16:39:30 +02:00
Ricki Hirner
8e7d289971 Tests: don't create account as long as Hilt is not ready (#939) 2024-07-24 15:13:34 +02:00
Ricki Hirner
d4c05b9282 NotificationRegistry: enforce creation of channels before they can be accessed 2024-07-24 15:13:06 +02:00
Ricki Hirner
768f462549 Use AccountRepository to get list of accounts (#938)
* Use AccountRepository to get list of accounts

* No AndroidEntryPoint in (Glance) widget
2024-07-24 14:50:48 +02:00
Ricki Hirner
cbd9a55c15 Replace some non-injected loggers 2024-07-24 09:53:31 +02:00
dependabot[bot]
4e496265e4 Bump dnsjava:dnsjava from 3.5.3 to 3.6.0 (#933)
Bumps [dnsjava:dnsjava](https://github.com/dnsjava/dnsjava) from 3.5.3 to 3.6.0.
- [Release notes](https://github.com/dnsjava/dnsjava/releases)
- [Changelog](https://github.com/dnsjava/dnsjava/blob/master/Changelog)
- [Commits](https://github.com/dnsjava/dnsjava/compare/v3.5.3...v3.6.0)

---
updated-dependencies:
- dependency-name: dnsjava:dnsjava
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-23 22:08:16 +02:00
Arnau Mora
73d0b63705 Debug info: Sync interval "0 min" when actually -1 (manual) (#928)
Added check for only manual sync

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-07-23 19:08:06 +02:00
Ricki Hirner
a361888d94 Version bump to 4.4.2-alpha.2 2024-07-23 01:11:39 +02:00
Ricki Hirner
907b38fd6a Better verbose log formatting 2024-07-22 00:18:52 +02:00
Ricki Hirner
bf0e169cf1 Remove obsolete Logger object (#930) 2024-07-21 23:32:45 +02:00
Ricki Hirner
fd2b3f0018 Refactor SettingsManager/SettingsProvider to use DI properly (bitfireAT/davx5#599)
* Refactor SettingsManager/SettingsProvider to use DI

* Fix tests

* Remove BaseDefaultsProvider

* Fix gplay tests
2024-07-20 21:47:15 +02:00
Ricki Hirner
6217582677 Debug builds: add StrictMode for ThreadPolicy, don't setup crash handler 2024-07-20 18:51:57 +02:00
Ricki Hirner
d03dc1f37d Remove unnecessary SyncComponent 2024-07-20 18:40:42 +02:00
Ricki Hirner
d33e4dcb23 Update dependencies 2024-07-20 18:35:31 +02:00
Ricki Hirner
f4c02d4ab6 [DI] Use @Inject lateinit var for abstract classes (#929)
* Use @Inject lateinit var for abstract classes (subclasses have @Inject constructor)

* Fix tests
2024-07-20 18:19:04 +02:00
Arnau Mora
fafa358dd8 DB: some missing indices (#890)
* Added index for `ownerId`

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Increased version number

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Increased version number

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added version

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added pushTopic as index

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Moved index

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Changed uniqueness

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Changed indexing method

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Moved `parentId` to independent index

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-07-20 17:35:39 +02:00
Ricki Hirner
d5d6592ae2 SyncWorker: use Provider for lazy injection (#925)
* SyncWorker: use Provider for lazy injection

* Use Provider to lazyily inject SyncAdapter
2024-07-19 18:09:20 +02:00
Ricki Hirner
ac48e65b1a Remove deprecated code / annotate as deprecated 2024-07-19 17:41:47 +02:00
Ricki Hirner
26f95db62a Re-factor Notifications and various Utils to DI (#924)
* [WIP] Use NotificationRegistry to post notifications

* Replace NotificationUtils by NotificationRegistry

* Re-factor SyncConditions; move tests to default location

* Describe notification channels
2024-07-19 17:10:29 +02:00
Ricki Hirner
5c4d697767 WebDAV cache: re-factor using Guava and Hilt (#921)
* Use per-resource Guava cache with weak keys and values as WebDAV page cache

* Replace ExtendedLruCache by Guava cache

* Use injected logger in DavDocumentsProvider

* Use DI for RandomAccessCallback, move Wrapper to outer class

* Fix tests

* Use Hilt for more classes

* Use Guava LoadingCache as page cache

* Fix tests

* Minor code change
2024-07-19 11:59:10 +02:00
Ricki Hirner
59a57fc40a Update dnsjava, use desugaring with nio, refactor DNS resolving (#917)
* Update dnsjava, use desugaring with nio, refactor DNS resolving

* KDoc

* Rewrite bestSRVRecord test

* Add pathsFromTXTRecords tests

* Add desugaring note

* Rename @AssistedFactory method to "create"

* testBestSRVRecord_MultipleRecords_Priority_Same: broaden range for distribution
2024-07-18 11:09:00 +02:00
Ricki Hirner
50c13e5b6d Install uncaught exception handler in a separate startup plugin (bitfireAT/davx5#597) 2024-07-18 10:21:31 +02:00
Ricki Hirner
3a38a06302 Version bump to 4.4.2-alpha.2 2024-07-17 12:41:50 +02:00
Ricki Hirner
c489002f5c Create interface for startup plugins (#915)
* Create StartupPlugin interface, implement with TasksAppWatcher (don't run during tests)

* KDoc
2024-07-16 14:45:40 +02:00
Ricki Hirner
7cbc9bd4f5 Sync: take URL from DB (Collection) instead of making a detour over other places 2024-07-16 12:14:10 +02:00
Ricki Hirner
1fd65a4d42 Use DI for AccountSettings and Android tests (#911)
* Use DI for AccountSettings

* AccountSettings: clarify different account variables

* Fix tests

* Inject @ApplicationContext instead of Application

* Add Hilt rule to various tests

* Fix tests

* Consequently use injected @ApplicationContext in Android tests; fix Hilt rules
2024-07-15 19:39:21 +02:00
Sunik Kupfer
b3cc24e4be Fix: Syncer synchronizes only once (#910)
* Use a separate HashMap to determine new remote collections

* Close provider after sync
2024-07-15 11:00:34 +02:00
Ricki Hirner
556741ae1e Get rid of Apache Commons (#901)
* Replace StringUtils

* Replace DateFormatUtils and NumberUtils

* Fix tests

* Replace ReflectionToStringBuilder

* Rewrite MemoryCookieStore to get rid of MultiMap

* Get rid of org.apache.commons.lang3.exception.ExceptionUtils

* Replace org.apache.commons.collections4.CollectionUtils

* Replace DigestUtils

* Replace ContextedException by SyncException

* Fix HttpClientTest

* SyncManager: remove obsolete code

* Fix StringUtilsTest

* DiskCache: add hit/miss logging

* DebugInfoModel: simplify PrintWriter generation
2024-07-14 10:22:00 +02:00
Ricki Hirner
b1bcf32535 Update AGP 2024-07-13 18:44:53 +02:00
Ricki Hirner
be43d360ba Refactor logging (#906)
* LogcatHandler: log source class as tag and not in the text, remove max length

* Separate LogManager and Logger usage

* Improve StringHandler for DavResourceFinder

* Tests, KDoc
2024-07-13 17:37:07 +02:00
Ricki Hirner
0adceb64ec Squashed commit of the following:
commit 60219aafc7
Author: Ricki Hirner <hirner@bitfire.at>
Date:   Thu Jul 11 23:59:54 2024 +0200

    Version bump to 4.4.1.1

commit 209fdf3e7c
Author: Ricki Hirner <hirner@bitfire.at>
Date:   Thu Jul 11 23:37:37 2024 +0200

    AccountSettings v16: re-enqueue periodic sync workers with correct class name (bitfireAT/davx5#593)
2024-07-12 12:30:19 +02:00
Ricki Hirner
5ebef30abb Tasks screen: adapt padding and recommendation (#898)
Tasks screen: adapt padding and text
2024-07-11 15:47:56 +02:00
Sunik Kupfer
3d65afbf8f Provide collection to SyncManager (#881)
* Provide collection to CalendarSyncManager

* Provide collection to ContactsSyncManager

* Provide collection to JtxSyncManager

* Provide collection to TasksSyncManager

* Provide collection to SyncManager

* Fix test

* Minor changes

* Add kdoc

* Minor changes

- added KDoc
- SyncManager can never handle address book authority (content
  authority for contacts is contacts authority)
- moved @UsesThreadContextClassLoader to respective sync manager

* Mock collection in test

* Add comments separating the sync process into separate steps

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-07-11 14:09:35 +02:00
Sunik Kupfer
51f01b215c Remove address books sync authority and content provider (#877)
* Drop address book provider

* Drop address book sync adapter service

* Replace address books authority with contacts authority

* Update kdoc

* Revert "Update kdoc"

This reverts commit dfb14d466f6c58d9422e59c770e0a5348a497b7d.

* Revert "Replace address books authority with contacts authority"

This reverts commit 0e15bf11b3235dfbc38696741100210fbe497dbd.

* Don't enable addressbook sync for main account

* Acquire content provider in Syncer

* Use contacts authority instead of address book authority when acquiring content provider

* Set default sync interval for address book authority again

* Minor re-ordering of lines

* Add comment, rename variable

* Move SyncAdapterServices out of adapter package

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-07-11 11:17:09 +02:00
Arnau Mora
c02bf942e4 Replaced "Create" by "Add" when creating accounts (#892)
Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2024-07-08 11:11:21 +02:00
Ricki Hirner
d7025d4e9e Use proper DI for Syncer implementations (#889) 2024-07-04 22:21:20 +02:00
Ricki Hirner
56f7b4bbc5 Replace Apache Commons by native calls/Guava (#883)
* Replace Commons IO by native calls/Guava

* Drop other Apache Commons

* Fix tests
2024-07-04 16:00:29 +02:00
Ricki Hirner
4a3ebc422f Version bump to 4.4.1 2024-07-02 17:03:37 +02:00
Ricki Hirner
4b18302ec7 Fix JtxSyncer entry point 2024-07-02 15:25:12 +02:00
Ricki Hirner
5dd7609524 Fix tests 2024-07-02 14:06:19 +02:00
Ricki Hirner
48855c7bb8 [DI] Use constructor injection for SyncManager sub-classes (#874) 2024-07-02 12:59:42 +02:00
Ricki Hirner
7a8761f703 Version bump to 4.4.1-rc.1 2024-07-01 21:58:21 +02:00
Ricki Hirner
1dd91a2848 Update dependencies 2024-07-01 21:55:09 +02:00
Ricki Hirner
90f1c015d2 Fix "force read-only address books" setting in UI (bitfireAT/davx5#587)
* AccountScreen: take "force read-only address books" setting into account

* Collection screen: take "force read-only address books" setting into account

* Add KDoc

* Small naming change
2024-07-01 21:50:15 +02:00
Ricki Hirner
726e20ed52 Show CalDAV/CardDAV tab when the respective service is present (#868)
Use existence of CalDAV/CardDAV service as condition for whether to show the respective tab
2024-07-01 12:28:35 +02:00
Ricki Hirner
7f750e22cb Split syncadapter package (bitfireAT/davx5#586)
* Split syncadapter package into:

- sync: actual collection sync code and everything else
- sync.account: related to Android accounts
- sync.framework: Android sync framework integration
- sync.groups: contact group strategies
- sync.worker: sync workers (WorkManager)

* Remove unused file, fix imports
2024-06-30 14:42:40 +02:00
Ricki Hirner
6b6573ddd2 [CI] Don't run lint/unit tests on release variant 2024-06-29 10:05:59 +02:00
Ricki Hirner
8849b363c7 Fetch translations from Transifex 2024-06-29 09:57:17 +02:00
Ricki Hirner
005d6b30c0 Version bump to 4.4.1-beta.1; update dependencies 2024-06-29 09:56:08 +02:00
Ricki Hirner
c3436fd23f Mark UnifiedPush setting as experimental 2024-06-27 19:54:28 +02:00
Ricki Hirner
5497e343c0 Add Github dependency graph 2024-06-27 18:46:14 +02:00
Ricki Hirner
479a2c363a Optimize build cache; use Gradle 8.8 2024-06-27 18:22:14 +02:00
Arnau Mora
18b1e5222e Fix GMD tests (#867)
* Run CI

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Changed GPU to swiftshader

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added more options

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Cleaning managed devices

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed cleanup of GMD

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Cleaning cache

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed app clean

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removal of cached system image

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Got rid of cached image removal

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Disabled build cache

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Disabled configuration cache

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Changed run command

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Downgrade AGP

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Restore to original

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Upgrade AGP again

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Add setupTimeoutMinutes=180 option

* Use large runner

* Update gradle, add maxConcurrentDevices=1

* Remove maxConcurrentDevices=1 again

* Use gradle 8.7 again, clean caches before

* Create "compile" task that caches dependencies etc.

* Don't use incremental build cache anymore

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-06-27 18:12:55 +02:00
Ricki Hirner
d4b4981e26 Version bump to 4.4.1-alpha.3 2024-06-25 22:22:09 +02:00
Ricki Hirner
e1f3785bc6 Add ProGuard rule to work around androidx-lifecycle 2.8.x + Compose 1.6 crash 2024-06-25 22:22:09 +02:00
Arnau Mora
e92f261faf Replace AppIntro by Compose Pager (#848)
* Migrated into to compose

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Got rid of AppIntro

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Imports cleanup

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Imports cleanup

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed padding

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* When launching intro, going back closes the app

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added content description

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Moved IntroActivity.Model to IntroModel

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Given fixed padding

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Moved intro composables together

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Do not create new task

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Minor changes

* Remove last XML styles that were required for AppIntro

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-06-25 15:12:25 +02:00
Ricki Hirner
ea035fa931 Version bump to 4.4.1-alpha.2 2024-06-25 14:52:51 +02:00
Ricki Hirner
8167e8e3cb Update dependencies 2024-06-25 14:49:57 +02:00
Ricki Hirner
bcc16e1ab6 Implement basic Push functionality (#856)
* Move PushRegistrationWorker to push package

* Add UP dependency

* [WIP] UnifiedPush basic implementation

* Handle endpoint unregistration

* [WIP] Parse push notification message

* Parse push message in PushMessageParser

* Sync only affected account on push message

* Only initiate sync when push message is about a syncable collection

* Push registration worker: log when there's no configured endpoint

* Handle invalid/non-XML push messages

* app settings: show UP endpoint
2024-06-25 14:07:29 +02:00
Ricki Hirner
28948485f6 Update AGP 2024-06-16 20:05:19 +02:00
Ricki Hirner
0985a99ed3 Update dependencies, remove unnecessary dependencies 2024-06-13 22:27:14 +02:00
Arnau Mora
1d7084b555 Add Push Subscription Management (#800)
* [wip] Create worker for push registration and call it from someplace in UI

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* [wip] Subscription registration request

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Don't enqueue push registration worker from UI

* Enqueue PushRegistrationWorker on collection changes

* Fix tests

* Update dav4jvm; Use new post method

* Remove obsolete context

* Add get and deleteAll methods to serviceRepository and update usages

* requestPushRegistration: make endpoint an argument

* Update push subscription fields in DB on successful registration

* Don't create notification channels in test class

* Remove workmanager init and provide empty set of listeners in tests

* Require network connection to run PushRegistrationWorker

* Move module declaration to a separate TestModules interface

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Sunik Kupfer <kupfer@bitfire.at>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-06-13 13:12:59 +02:00
Sunik Kupfer
64563bbd3a Observable collections repository (#829)
* Call collection updates and setters only from repository

* Make collection repository changes observable

* Add kdoc

* Add basic test

* Extract RefreshCollectionsWorker; move some HomeSetDao calls to DavHomeSetRepository

* Replace more HomeSetDao calls

* Remove duplicate copyright notice

* Drop weak reference for observers

* Rename method

* Remove test service after run

* Verify notifying works with mockk

* Rename test

* Use construction injection

* Remove unused SettingsManager

* Remove obsolete mockk rule

* Use runBlocking instead of runTest

* Change to observer linkedList to mutableSetOf, remove synchronized calls

* Change to hilt multibinding

* Remove some unnecessary lines; allow empty set by Hilt

* CollectionListRefresher: delete collections using repository

* deleteRemote: call callback too; adapt KDoc

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-06-12 12:43:56 +02:00
Ricki Hirner
0bdeffe70d [Github] Issue template: fix label of feature request 2024-06-12 11:39:26 +02:00
Ricki Hirner
9e0772a9dd Update AGP 2024-06-11 09:21:35 +02:00
Ricki Hirner
70e56df80c Move repository test package 2024-06-10 13:40:33 +02:00
Ricki Hirner
b4756666b6 Extract RefreshCollectionsWorker; move some (HomeSet)Dao calls to repository (#845)
* Extract RefreshCollectionsWorker; move some HomeSetDao calls to DavHomeSetRepository

* Replace more HomeSetDao calls

* Remove unused SettingsManager
2024-06-10 12:16:33 +02:00
Ricki Hirner
74304bfe17 Version bump to 4.4.1-alpha.1 2024-06-09 23:02:23 +02:00
Ricki Hirner
25daa57a6f Show CalDAV/CardDAV/Webcal tab when there's at least one item (#839)
Show CalDAV/CardDAV/Webcal tab when there's at least one time
2024-06-08 18:44:35 +02:00
Ricki Hirner
a3ccdc2a46 Define toolchain version; update dependencies (#834) 2024-06-08 18:09:45 +02:00
Arnau Mora
ffefd519b6 [Google] Allow login with custom domains again (#833)
Modified emailWithDomain in UiState

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-06-07 10:11:00 +02:00
Ricki Hirner
4823d6d671 [CI] Update gh-release action 2024-06-05 11:45:10 +02:00
Ricki Hirner
9b5d5c982b Version bump to 4.4.0.1 2024-05-31 08:49:25 +02:00
Ricki Hirner
a03c83450d App settings: mention that verbose logs can be viewed in debug info (#824) 2024-05-31 08:47:52 +02:00
Ricki Hirner
5d97161c9b Update dependencies (including ical4android, closes bitfireAT/davx5#551) 2024-05-31 08:11:32 +02:00
Ricki Hirner
671c17376a PreferenceRepository.observeAsFlow: emit initial value (#822) 2024-05-30 10:09:34 +02:00
Ricki Hirner
1856a5d7ce Fix R8 rules 2024-05-29 17:25:05 +02:00
Ricki Hirner
9cb78429e3 Fetch translations from Transifex 2024-05-29 17:10:32 +02:00
Ricki Hirner
2b17692fd6 Version bump to 4.4 2024-05-29 17:07:10 +02:00
Ricki Hirner
19e69f2079 Version bump to 4.4-rc.1 2024-05-28 23:39:38 +02:00
Ricki Hirner
72b90655e6 Update dark theme 2024-05-28 23:38:18 +02:00
Ricki Hirner
cdf83dad37 MKCALENDAR: wrap supported components in <CALDAV:supported-calendar-co… (#816)
* MKCALENDAR: wrap supported compoents in <CALDAV:supported-calendar-component-set>

* MKCALENDAR/MKCOL body generation: use Property.Name from dav4jvm instead of own strings
2024-05-28 21:05:09 +02:00
Ricki Hirner
e6be7a659f Last M3 Tweaks (#817)
* [WIP] Colors

* Update navigation drawer

* Update colors

* [WIP] PermissionSwitchRow night mode

* Fix PermissionSwitchRow icon in night mode

* Use more intense colors for FABs
2024-05-28 20:58:51 +02:00
Ricki Hirner
34052368d8 Issue template: add "actual result" 2024-05-28 14:29:48 +02:00
Sunik Kupfer
08c695bf05 Fix last sync time not being set for address books (closes #810) (#812)
* Use correct account name when retrieving service

* Rename method, add kdoc

* Address books: require main account and use as Account

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-05-27 17:00:32 +02:00
Arnau Mora
d7221974ed Increased icon size and text size for accounts list (#813)
* Increased icon size and text size

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Adapt

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-05-27 16:28:34 +02:00
Arnau Mora
9a31835b1c Fix color picker size (#808)
* Fix color picker size

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed missing comment & import optimization

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added border

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Color picker: use OutlinedButton

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-05-27 14:53:39 +02:00
Ricki Hirner
4a49c8d6c2 Update dependencies 2024-05-25 21:26:09 +02:00
Sunik Kupfer
c493fbd349 Orange progress indicator bar (#803)
* Create ProgressBar composable with secondary default color

* Minor change

* Add import

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-05-25 20:21:33 +02:00
Arnau Mora
71c57fc00d Fix padding for show only personal (#807)
* Fixed padding for "Show only personal checkbox"

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Aligned text correctly

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-05-25 20:15:58 +02:00
Ricki Hirner
3044ff70aa Increase version code 2024-05-17 18:05:42 +02:00
Ricki Hirner
76a47fd017 Update theme and style again to make it look like it used to look 2024-05-17 18:04:56 +02:00
Ricki Hirner
621a8c419b Fetch translations from Transifex (again) 2024-05-17 15:19:18 +02:00
Ricki Hirner
c4cf0dc07b Fix English string (remove duplicate end tag) 2024-05-17 14:59:55 +02:00
Ricki Hirner
9216605106 Version bump to 4.4-beta.1 2024-05-17 14:18:48 +02:00
Ricki Hirner
d802d67e22 Fetch translations from Transifex 2024-05-17 14:18:28 +02:00
Sunik Kupfer
f95b853248 Fix setup through nextcloud app (intent) not working (#782)
* Select nextcloud login type on nextcloud setup intent

* Fix linting error

* Add documentation

* Move model creation to compose LoginScreen

* Minor changes
- Use boolean to decide on skipping startPage
- Move login type selection logic to login types provider

* Minor changes

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-05-17 14:10:28 +02:00
Ricki Hirner
3bcecd4fd6 Try different M3 theme 2024-05-17 13:10:39 +02:00
Ricki Hirner
b93b63024c Fix widget colors (#799) 2024-05-17 10:51:09 +02:00
Ricki Hirner
d9610b26bb Repair drawable/ic_storage_notify 2024-05-16 18:33:51 +02:00
Ricki Hirner
7c43bcc558 Version bump to 4.4-alpha.1 2024-05-16 18:22:17 +02:00
Ricki Hirner
30f9b117cc Remove unnecessary resources 2024-05-16 18:19:46 +02:00
Ricki Hirner
f2255e1d53 Drop M2 (everything is M3 now) (#797)
Drop M2
2024-05-16 17:56:18 +02:00
Sunik Kupfer
e2bfa8c56b Rewrite AccountSettingsActivity to M3 (#795)
* Extract composables

* Drop sub component previews and minor adjustment

* Fix preview

* Extract view model

* Switch to M3

* Extract URI to Constant

* Minor changes

* We alway have AccountSettings

* Replace LiveData by State

* Use Snapshot.withMutableSnapshot in reload

* Don't show empty OAuth setting

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-05-16 17:34:11 +02:00
Ricki Hirner
5cc29fc58a M3 tweaks (#796)
* Refine chips in collection list

* Refine PermissionSwitchRow

* Collections list

* Fix WelcomePage and IntroActivity background color in dark mode

* Fix RadioWithSwitch in dark mode

* Drawer handler: branding in dark mode
2024-05-16 16:45:40 +02:00
Sunik Kupfer
6f02669832 Rewrite AppSettingsActivity to M3 (#792)
* Extract composables

* Extract model and companion object

* Switch to M3

* Linting

* Drop previews for sub composables

* Minor adjustments for readability

* Minor changes

- use manual URL from Constants
- use M3 in some Composables

* Create PreferenceRepository (for now only for verbose logging)

* Move actual settings to model; M3 Composables

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-05-16 13:00:20 +02:00
Ricki Hirner
e11d511971 Update dependencies 2024-05-16 11:46:26 +02:00
Ricki Hirner
00eeb0e6d5 More DI using Hilt (#789)
* [WIP] More hilt

* Use assisted inject for AccountSettingsMigrations

* DavDocumentsProvider: inject CredentialsStore

* Create a new WebdavScope and scope caches and credentials store to it

* Fix CredentialsStoreTest
2024-05-15 17:57:41 +02:00
Sunik Kupfer
814d19e698 Rewrite WifiPermissionsActivity to M3 (#791)
* Extract composables

* Extract view model and create preview

* Switch to M3 and linting

* Optimize imports

* Minor changes

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-05-15 16:57:48 +02:00
Sunik Kupfer
86252f9117 Rewrite DebugInfoActivity to M3 (#744)
* Convert M2 calls to M3

* Extract composable to screen

* Extract viewmodel

* Make screen model independent

* Use only primitive types in screen

* Introduce uiState class and switch to compose state where easy

* Switch remaining live data to compose state

* Add kdoc

* Add scrolling, adapt buttons to M3

* Move Intent logic to Activity

- create/handle Intent in Activity (may be replaced by NavGraph in future)
- Activity: pass unpacked initial data to Screen
- Screen: use hiltViewModel (adds hilt-navigation-compose dependency) to
  create model with initial data
- Screen: use Column instead of LazyColumn

* Fix test

* Optimize imports

* Minor changes

* Move AppTheme, fix showDebugInfo

* View instead of share logs; make local/remote resource smaller; make remote resource selectable

* Leave space for scrolling down past the FAB; don't show "Local resource: null"

* Re-order composables

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-05-15 16:43:23 +02:00
Arnau Mora
b5334887e8 Added M3 theme to intro pages (#756)
* Added M3 theme

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Don't use M2 colors anymore

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Sunik Kupfer <kupfer@bitfire.at>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-05-15 15:59:53 +02:00
Arnau Mora
6b863164a4 Rewrite BatteryOptimizationsPage to M3 (#747)
* Migrated to M3

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Arch update for best practises

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Moved composable to individual file

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Renamed function

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Moved ViewModel

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Moved ViewModel

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Moved ViewModel-based Composable

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Created `UiState`

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed model name

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Typo

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed checkboxes

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Make checkboxes clickable too

* Optimized imports

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Optimize imports

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Sunik Kupfer <kupfer@bitfire.at>
2024-05-15 14:11:22 +02:00
Sunik Kupfer
b4e58eeb44 Log last sync time per collection service and URL (#702)
* Log last sync time per collection service and URL

* Remove unused deprecated method

* Include tasks and other services as caldav type
2024-05-14 21:28:18 +02:00
Arnau Mora
a246046f41 AccountSettingsActivity: make canAccessWifiSsid live-capable (#732)
* Added `canAccessWifiSsidLive`

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed initial state

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Cleaned up code

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* More cleanup

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed inspection check

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Renamed function

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Using `broadcastReceiverFlow`

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Simplified check

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Using `MODE_CHANGED_ACTION`

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Updated comment

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Set default value for immediate

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Use derivedStateOf instead of produceState; correctly collect broadcastReceiverFlow

* Always show WifiSSID Card in Preview (otherwise Preview won't render)

* Don't call flow.map in Composable

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-05-11 14:49:56 +02:00
Ricki Hirner
0552bcab4a Version bump to 4.3.17-alpha.4 2024-05-09 21:28:38 +02:00
Ricki Hirner
3681507582 Inject SyncDispatcher over Hilt (#784)
* Inject SyncDispatcher over Hilt

* Use setWorkerFactory for TestListenableWorkerBuilder

* Correctly inject SyncDispatcher over an AssistedFactory
2024-05-09 21:20:38 +02:00
Arnau Mora
364f372a8b Rewrite TasksIntroPage to M3 (#760)
* Migrated to M3

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Fixed theme

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Moved Composables and Model to individual files

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Move AppTheme to screen composable

* Fixed model name

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Minor re-ordering

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Sunik Kupfer <kupfer@bitfire.at>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-05-09 18:51:38 +02:00
Ricki Hirner
857309c451 Correctly handle DeadObjectException (bitfireAT/davx5#578, #591)
* Soft fail sync on DeadObjectException so that it is retried without immediate error message

* Handle DeadObjectException (→ retries sync); Syncer: generalize all-catch
2024-05-09 13:52:50 +02:00
Sunik Kupfer
ad24dd54c7 Fix login detail pages loosing user changes on rotation / re-creation (closes #775) (#778)
Inject view models in composables using hilt
2024-05-07 10:17:25 +02:00
Ricki Hirner
952cb52b95 Don't listen for account changes all the time (#780)
* Delete account: don't rely on cleanup worker

* Don't list for account changes all the time
2024-05-04 20:52:50 +02:00
Ricki Hirner
1ad8e892b6 Intro pages: use Hilt for dependency injection (#779)
* Accounts: move syncAll to model; better Hilt usage

* Move calculation of whether IntroActivity is shown into AccountsModel

* Intro pages: inject dependencies with Hilt

* Fix preview
2024-05-04 20:10:36 +02:00
Arnau Mora
4cffbe7b40 Rewrite PermissionsIntroPage to M3 (#758)
* Migrated to M3

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Replaced preview theme

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Fixed theme

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Forced all sets to be private

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Moved Composables and Model to individual files

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Fixed naming

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Theme / M3 changes

* Observe lifecycle from within Screen

* Minor changes

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-05-04 13:46:32 +02:00
Ricki Hirner
e6eb90861e Use hiltViewModel for creating ViewModels with parameters in new Screen architecture (#776)
* Add hilt-navigation-compose dependency

* AccountScreen: use hiltViewModel

* Use hiltViewModel for CollectionScreen and CreateCollectionScreens
2024-05-03 14:46:46 +02:00
Ricki Hirner
9005121b52 Update dependencies (including AGP) 2024-05-03 12:23:58 +02:00
Arnau Mora
a7c04c2bf7 Replace DavUtils.lastSegmentOfUrl by UrlUtils.lastSegment (#767)
* Added `ReplaceWith` to `lastSegmentOfUrl`

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Replaced all usages

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Rollback replacement

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added tests for UrlUtils

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Got rid of `DavUtils.lastSegmentOfUrl`

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Turned into variable

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed method call

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Fixed method call

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-05-03 11:29:05 +02:00
Ricki Hirner
39f8f2e475 Add issue templates 2024-05-02 14:03:01 +02:00
Sunik Kupfer
9c6c95a249 Start at LoginDetails page if logging in via intent (#772)
* Start at LoginDetails page if login via intent has data

* Ignore intent on re-creation
2024-05-02 12:43:08 +02:00
Sunik Kupfer
aafcb2e94a Pre-select per-contact categories for login type NextcloudLogin (#774)
* Pre-select per-contact categories for login type NextcloudLogin

* Update the group method unconditionally to suggested group method
2024-05-02 12:32:23 +02:00
Arnau Mora
e40fa6e0fb Switched CalDAV and CardDAV tab positions (#769)
Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-05-02 11:18:30 +02:00
Arnau Mora
c9fd66bd63 Rewrite OpenSourcePage to M3 (#755)
Migrated to M3

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2024-05-02 11:11:51 +02:00
Arnau Mora
99cf0eca7a Migrated to M3 (#754)
Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2024-05-02 11:08:37 +02:00
Ricki Hirner
2b2476b4bc Rewrite RenameAccountDialog to M3 (#768) 2024-04-30 14:37:12 +02:00
Arnau Mora
81834ca0db AccountsScreen: move navigation drawer to foreground so that it overlays FABs when open (#765)
Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-04-30 14:20:52 +02:00
Ricki Hirner
0a4a06a50a Minor lint 2024-04-30 11:49:01 +02:00
Ricki Hirner
40795bf5c0 Version bump to 4.3.17-alpha.3 2024-04-30 11:40:40 +02:00
Ricki Hirner
40034fe400 Update dependencies 2024-04-30 11:27:26 +02:00
Ricki Hirner
b13c6b0e6f Rewrite AccountActivity to M3 (#752)
* [WIP] Separate AccountScreen, use M3 elements

* [WIP] Use UseCases for complex flow calculations

* [WIP] Move account deletion logic into AccountRepository

* Move rename operation to repository

* Adapt FABs

* Don't use snackbars to show when the collection list is refreshed

* New collection list layout (bitfireAT/davx5#159)

* [WIP] Create AccountModel from within screen

* [WIP] Clean up AccountScreen

* [WIP] CreateAddressBook

* [WIP] Create address book / calendar screen

* [WIP] Begin CollectionScreen

* [WIP] CollectionScreen

* Error handling

* String resources

* Optimizations, remove unnecessary things
2024-04-30 11:25:29 +02:00
Ricki Hirner
c33ea84c77 Rewrite AccountsActivity to M3 (#749)
* [WIP] Rewrite AccountsActivity to M3

* [WIP] AccountsScreen: FAB, previews

* [WIP] Warning cards

* Adapt FABs
2024-04-26 11:20:55 +02:00
Ricki Hirner
0c748ebe73 Provide snackbar to LoginTypesProvider (backport of bitfireAT/davx5#576) 2024-04-24 10:31:46 +02:00
Ricki Hirner
fda96ac653 Version bump to 4.3.17-alpha.2 2024-04-23 17:42:07 +02:00
Ricki Hirner
597c572f24 Login: allow changing account name when suggestions are expanded (bitfireAT/davx5#573) 2024-04-23 17:42:03 +02:00
Ricki Hirner
71b0912494 Login: allow "Couldn't create account" snackbar more than once (bitfireAT/davx5#572) 2024-04-23 17:41:59 +02:00
Ricki Hirner
f853019f47 LoginScreenModel: initialize account name over setter (bitfireAT/davx5#571) 2024-04-23 17:41:54 +02:00
Ricki Hirner
0b212fc6bd WebDAV Mounts UI: M3, refactoring (#736)
* WebDavMountsScreen: M3, refactoring

* [WIP] AddWebDavMount

* Use M3 pull-to-refresh

* AddWebDavMount, move logic to WebDavMountRepository

* Show loading state in LinearProgressBar

* Add WebDAV mount: IME action and focus requester

* Move "test WebDAV" logic from model to repository

* Move "refresh quota" logic to repository

* Move querying and deleting mounts to repository; IME navigation

* KDoc

* Move hasWebDav tests to repository
2024-04-23 17:34:20 +02:00
Sunik Kupfer
3fbffc4a72 RefreshCollectionsWorker causes app to crash when the service is invalid (#743)
* RefreshCollectionsWorker: Ignore requests with invalid service IDs

* Update KDoc
2024-04-22 17:31:55 +02:00
Ricki Hirner
b17c39c370 Version bump to 4.3.17-alpha.1; fix LoginActivity upwards navigation 2024-04-21 16:51:16 +02:00
Ricki Hirner
019dde6ef9 Rewrite login to M3 and with better UI states (bitfireAT/davx5#567)
* [WIP] Rewrite login to M3 and better UI states

* Use AccountRepository to create account

* LoginModel is SSOT for page navigation

* Support forced group method

* Show progress bar when account is being created

* Make account name suggestions work again

* Use M3 text field supportText for errors

* Refactor: login by URL, login by email, advanced login

* Refactor Nextcloud login, move login flow logic to separate class

* Refactor Google login, move OAuth logic to separate class

* Fix errors when navigating back after successful resource detection

* Make PasswordTextField M3

* ManagedLogin: M3, UiState

* Updated theme; managed login functionality

* Improve back navigation
2024-04-21 16:27:54 +02:00
Ricki Hirner
34b88c3ad8 Add M3 theme and apply to AboutActivity (#731)
* Rename AppTheme to M2Theme, add M3 theme

* Rewrite AboutActivity to M3

* Apply M3 theme; minor optimizations

* Use M3 version of AboutLibraries

* Use material3 instead of material3-android dependency

* Use reversed theme
2024-04-19 11:05:39 +02:00
Ricki Hirner
a1d85c4c9b Version bump to 4.3.16.1 2024-04-17 13:25:52 +02:00
Ricki Hirner
8c12300be3 Update dav4jvm to support relaxed parsing (bitfireAT/davx5#563)
* Update dav4jvm

* Add test
2024-04-17 13:25:48 +02:00
Ricki Hirner
21d1020662 Add PR template 2024-04-16 20:10:58 +02:00
Ricki Hirner
80a54de015 Add PR labels for automatic release notes 2024-04-15 11:43:42 +02:00
Ricki Hirner
a3a4a72012 Fix test_observerFlow_updatedValue (use Set instead of List) 2024-04-15 11:12:02 +02:00
Ricki Hirner
a165c97166 Version bump to 4.3.16 2024-04-14 21:55:33 +02:00
Arnau Mora
bd93c86e2c Adjusted paddings (#707)
* Adjusted paddings

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Unify intro card padding to 8 dp

* Minor changes in Composable calls

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-04-14 21:42:52 +02:00
Ricki Hirner
ab4244a533 Allow to specify export flags for broadcast receivers; log incoming broadcasts (#729) 2024-04-14 21:17:19 +02:00
Ricki Hirner
0a58f0a269 Always use provider.use to automatically close content provider clients (#726) 2024-04-13 22:03:42 +02:00
Ricki Hirner
f6d71ffb98 Fetch translations from Transifex 2024-04-13 21:19:42 +02:00
Ricki Hirner
0ff24a8ce2 Version bump to 4.3.16-rc.1 2024-04-13 21:05:32 +02:00
Ricki Hirner
114084f405 Use LocalContentColor for ClickableTextWithLink (#725) 2024-04-12 18:02:25 +02:00
Ricki Hirner
c3b3dd4e35 Add non-Compose colorBackground for AppIntro (#724) 2024-04-12 17:29:55 +02:00
Ricki Hirner
cd8023b24b [CI] Make sure concurrency name doesn't have spaces 2024-04-12 17:18:27 +02:00
Ricki Hirner
ee36753e1a TasksActivity: use defaultValue=true for HINT_OPENTASKS_NOT_INSTALLED (#723) 2024-04-12 17:08:37 +02:00
Ricki Hirner
c0570549c9 Use broadcastReceiverFlow instead of BroadcastReceivers (#722)
* [WIP] Use broadcastReceiverFlow and packageReceiverFlow

to make sure that broadcast receivers are unregistered

* Rewrite remaining BroadcastReceivers to use broadcastReceiverFlow

* TasksAppWatcher: minor coroutine changes

* KDoc

* TasksActivity: use Compose state instead of LiveData
2024-04-12 16:52:23 +02:00
Ricki Hirner
463c18c4fb Update dependencies, including AGP 2024-04-10 14:10:12 +02:00
Sunik Kupfer
72320b30f7 Check if keep-permissions changed when user comes back from settings app (#715)
* Check if keep-permissions changed when user comes back from settings app

* Use one model only

* Use mutable state flows

* Use compose mutable state

* [CI] Improve caching behavior

* Freshly created model can never be null

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-04-10 14:06:48 +02:00
Ricki Hirner
dd08415c84 [CI] Improve caching behavior 2024-04-10 13:54:23 +02:00
Ricki Hirner
b88c35169e SettingsManager: use Flows instead of LiveData (#714)
* SettingsManager: use flows instead of LiveData

* Fix tests
2024-04-10 12:10:03 +02:00
Ricki Hirner
1cd0df1e6a Rename dev-ose branch to main-ose 2024-04-09 18:16:10 +02:00
Ricki Hirner
74e89bde9b Version bump to 4.3.16-beta.2 2024-04-09 17:56:41 +02:00
Ricki Hirner
b72cc7f9fb [CI] Fix test workflows (main branch condition, cache names) 2024-04-09 17:44:18 +02:00
Ricki Hirner
3023a935a8 Detect WebDAV-Push support (#716)
* Detect WebDAV-Push support

- detect and save supportsWebPush and pushTopic to database

* Collection info: show Push support + subscription time
2024-04-09 17:31:02 +02:00
Ricki Hirner
2c5292a52b Update dependencies 2024-04-09 15:38:41 +02:00
Ricki Hirner
a4ce1f93f0 Nextcloud Login Flow: improved handling of back (#712)
* Use Compose state instead of LiveData; reset token before starting Login flow

* [WIP] Reset state variables when needed

* Always run postForJson in IO dispatcher; show progress when polling for login data

* Reset login URL in onReturnFromBrowser
2024-04-09 11:52:49 +02:00
Ricki Hirner
30d4aa2e73 [CI] Fix tests 2024-04-09 10:33:38 +02:00
Ricki Hirner
e638f5debc Create account: user feedback when account name is already taken (#701)
* Show snackbar when account can't be created without known reason; show error when account name is taken

* Don't crash on empty account name
2024-04-04 13:36:27 +02:00
Ricki Hirner
7b2f14d148 Use normal runner for tests with emulator
See https://github.blog/changelog/2024-04-02-github-actions-hardware-accelerated-android-virtualization-now-available/
2024-04-03 12:38:56 +02:00
Ricki Hirner
3385e5ddb7 Fix tests 2024-04-02 10:58:37 +02:00
Ricki Hirner
44d2446d22 Better handling of address book accounts without main account (closes #694) 2024-04-01 21:26:02 +02:00
Ricki Hirner
6b88052e10 DeleteCollectionDialog: handle case that result may contain null value (#697) 2024-04-01 21:10:53 +02:00
Ricki Hirner
6d462ea9e4 Update gradle, Hilt 2024-04-01 13:32:50 +02:00
Ricki Hirner
10b007bd15 Version bump to 4.3.16-beta.1 2024-03-28 14:26:57 +01:00
Ricki Hirner
9b7a1dbe87 Update ical4android 2024-03-28 13:52:37 +01:00
Sunik Kupfer
6ff0ebc7e2 Do not show existing password on password change (#689)
* Do not show existing password when changing

* Use PasswordTextField for password input dialogs

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-03-28 13:34:32 +01:00
Ricki Hirner
9b47427b66 SettingsLiveData: post initial value even when it's null (#686) 2024-03-28 09:44:19 +01:00
Ricki Hirner
d0e5bbc0ad Update copyright; upgrade dependencies (#687) 2024-03-27 17:32:35 +01:00
Ricki Hirner
f825abca27 Login screen: increase padding of login options minimally 2024-03-27 17:27:46 +01:00
Arnau Mora
7b12a53616 Create a native Material theme and get rid of XML styles (#675)
* Added custom Compose theme

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Got rid of accompanist theme adapter

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed unused import

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added actionbar to the activities that didn't have one

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Theme now always hides actionbar

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added back string

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Got rid of all color definitions

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Moved color definition to drawable

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Using Compose colors

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Using AppCompat theme

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed XML theme

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Moved color definition

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added bars coloring in Compose

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added dark theme

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added custom Compose theme

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Got rid of accompanist theme adapter

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed unused import

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added actionbar to the activities that didn't have one

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Theme now always hides actionbar

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added back string

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Got rid of all color definitions

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Moved color definition to drawable

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Using Compose colors

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Using AppCompat theme

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed XML theme

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Moved color definition

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added bars coloring in Compose

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added dark theme

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Changed content description

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Using `onSupportNavigateUp`

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Changed color on top of primary green

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added up navigation

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Made `onSupportNavigateUp` optional

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Typo

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Got rid of edge-to-edge

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added back some XML styles

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Simplify TasksCard calling

* Move theme colors to flavor-specific ThemeColors file

* Remove global AppTheme paddings for now

* Optimize imports

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-03-27 17:21:02 +01:00
Ricki Hirner
8507c64be1 OpenSourcePage: make "dontShow" LiveData (#683) 2024-03-27 14:42:44 +01:00
Sunik Kupfer
557b5653e5 Don't show empty suggested account names dropdown (bitfireAT/davx5#555)
* Don't show empty suggested account names dropdown

* Hide the trailing dropdown icon if suggested account names list is empty
2024-03-27 13:04:50 +01:00
Ricki Hirner
7760cbaa72 Show Pending state in AccountsActivity, too; remove obsolete code (#680) 2024-03-27 10:13:52 +01:00
Sunik Kupfer
14ef74e231 Don't move focus downward without reason (bitfireAT/davx5-ose#553) 2024-03-26 10:20:45 +01:00
Ricki Hirner
78c148615a Fix ProGuard rules 2024-03-24 21:45:44 +01:00
Ricki Hirner
b63cd67e9c Backport changes from build variants 2024-03-24 21:30:08 +01:00
Ricki Hirner
079c3efdfd Rewrite login activity to Compose (#672)
* Remove unnecessary layout files

* [WIP] Rewrite LoginActivity to Compose

* [WIP] Login type

* [WIP] Login by URL, Google, Nextcloud

* Remove unnecessary files and kapt

* More renaming and removing of unnecessary files

* Login with email, URL

* Login type: Advanced

* Drop "known base URLs"

* "Detect resources" and "Create account" page

* Introduce LoginTypesProvider interface
2024-03-24 18:25:30 +01:00
Michael Biebl
014c94a031 Do not show extended proxy details in App Settings if proxy type is system or none (#661)
Fixes: #660
2024-03-21 11:44:03 +01:00
Arnau Mora
5921fb2bb6 Upgraded ical4android (#663)
Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-03-21 11:43:44 +01:00
Arnau Mora
58b02c04ef Replaced icons with auto-mirrored version when possible (#666)
Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-03-21 11:41:47 +01:00
Ricki Hirner
06e7eeb391 Don't use Gradle build and configuration cache for releases (#662)
* Don't use Gradle build and configuration cache for releases

- don't enable Gradle build and configuration cache for the project, but recommend it for the developer
- explicitly enable Gradle build and configuration cache for CI test jobs
- let AboutLibraries generate lib definitions itself again
- also don't archive test results (sometimes fails and we never use the results)

* Add encryption key for gradle cache

* Only warn on configuration cache problems (caused by AboutLibraries)
2024-03-20 15:29:21 +01:00
Michael Biebl
6c882877d0 Remove unused string resources (#651)
The list was computed in an automated way and includes:

intro_battery_not_whitelisted
intro_battery_whitelisted
permissions_jtx_status_off
permissions_jtx_status_on
about_flavor_info
about_translations_thanks
install_email_client
accounts_global_sync_disabled
accounts_global_sync_enable
app_settings_tasks_provider_synchronizing_with
account_no_address_books
account_no_calendars
account_no_webcals
account_swipe_down
account_create_new_address_book
account_create_new_calendar
settings_title
settings_enter_username
settings_enter_password
settings_key_default_alarm
certificate_notification_connection_security
trust_certificate_unknown_certificate_found
2024-03-20 15:24:00 +01:00
Ricki Hirner
d8e6f82104 Fix import 2024-03-20 15:17:20 +01:00
Arnau Mora
28ddf5c86a Provide a widget option (#643)
* Added Glance dependency

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Defined Glance widget

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Replaced with new worker

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Rename ui/widget to ui/composable, move sync widget into ui/widget; adapt strings

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-03-20 12:37:57 +01:00
Ricki Hirner
c3bf95fa5c Update AGP to 8.3.1 2024-03-20 11:41:13 +01:00
Arnau Mora
d37718c58a Rewrite CreateCalendarActivity to Compose (#645)
* Added color picker dialog

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed title

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Migrated to Jetpack Compose

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed unused layouts

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed activity name

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed duplicated title

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Cleanup

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Error tweaks

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Rewrite Create address book, Create collection, Delete collection

* [WIP] Create calendar: more properties; use own color picker (other one was M3)

* [WIP] Create calendar

* Add missing properties for calendars

* Support color, remove comments

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-03-20 11:40:03 +01:00
Ricki Hirner
fea33ab60a Version bump to 4.3.15 2024-03-16 15:18:33 +01:00
Ricki Hirner
3ab278a315 Better handling of tasks app changes (#652)
* Fix task sync update when new app is selected by user or TasksWatcher
* Minor refactoring, KDoc
2024-03-16 12:16:23 +01:00
Ricki Hirner
322a7565b0 TaskUtils: take updateSyncSettings into account 2024-03-16 00:26:15 +01:00
Ricki Hirner
732d925b4c Show pending syncs in AccountActivity again 2024-03-15 12:08:40 +01:00
Ricki Hirner
a43dbb5cff Have network/WiFi check in both PeriodicSyncWorker and OneTimeSyncWorker; add tests
- OneTimeSyncWorker is also started by sync framework, so it should take network restrictions into consideration
- add "manual" flag for manual syncs that ignore network restrictions
2024-03-15 11:30:54 +01:00
Ricki Hirner
fe833759ee Fetch translations from Transifex 2024-03-15 10:38:37 +01:00
Ricki Hirner
53bf342822 Version bump to 4.3.15-beta.1 2024-03-14 20:24:36 +01:00
Ricki Hirner
30122a79f3 Periodic workers directly run sync (#648)
* [WIP] Don't create a separate SyncWorker for every sync (run directly within onetime/periodic sync instead)

* [WIP] address books

* Account(s)Activity: don't show pending workers

* Migration to set new periodic sync worker tags

* Fix tests

* ContactsSyncAdapter issues address book sync on main account (not contacts sync)

* SyncAdapterService: optimize blocking with Flow instead of LiveData
2024-03-14 20:08:38 +01:00
Ricki Hirner
a16fd468fd TasksIntroPage: fix "Dont show again" 2024-03-13 17:19:23 +01:00
Ricki Hirner
76df2b320d Account settings UI: fix "sync only manually" summary 2024-03-13 17:03:50 +01:00
Ricki Hirner
d6ff27fcc8 Fix up navigation 2024-03-13 17:02:27 +01:00
Ricki Hirner
2e780a890b Rewrite AccountSettingsActivity to Compose (#646)
* AccountSettings: rewrite Sync settings
* Authentication
* CalDAV, CardDAV settings
2024-03-12 20:48:06 +01:00
Ricki Hirner
45d6b33023 Add stats parameters for Web site calls, rename AccountActivity2 back to AccountActivity 2024-03-12 15:41:50 +01:00
Ricki Hirner
4486c0862a Move "Community" to "Support the project", remove obsolete code 2024-03-12 15:41:41 +01:00
Ricki Hirner
33e726a7b0 Rewrite navigation drawer to Compose (#644)
(sync bitfireAT/davx5#545)
2024-03-12 12:55:38 +01:00
Ricki Hirner
75a0c77b5f Rewrite WifiPermissionsActivity to Compose (#640) 2024-03-11 16:23:25 +01:00
Ricki Hirner
06b4cf9477 Refactor Tasks app detection (and settings update when tasks apps change) (#637)
* Refactoring

* Better live handling of (un)installed task apps

* Minor changes

* SettingsManager: explicitly mark possibility of null LiveData values

* Fix tests
2024-03-11 13:51:46 +01:00
Ricki Hirner
cb56132994 Make LinearProgressIndicators orange (#636) 2024-03-10 23:08:22 +01:00
Ricki Hirner
9742913a3e App settings: show "Battery optimizations" dialog even when DAVx5 is already exempted 2024-03-10 22:44:52 +01:00
Ricki Hirner
ea66838cd6 Version bump to 4.3.15-alpha.2 2024-03-10 20:52:33 +01:00
Ricki Hirner
377c0344da Improve homepage URL launching (#632)
* Move homepage and other Web URLs to Constants; minor refactoring

* Use AppTheme with built-in safe LocalUriHandler instead of MdcTheme; minor refactoring

* Account settings: add TODO for Compose rewrite

* Use UriHandler instead of UiUtils.launch when possible
2024-03-10 20:51:40 +01:00
Ricki Hirner
af5c732adc Update dependencies 2024-03-10 19:27:37 +01:00
Ricki Hirner
c8a0128842 Rewrite app settings to Compose (#628)
* [WIP] Rewrite app settings to Compose

* Optical changes

* Add Help button

* Fix URL, preferences LiveData: handle null value

* Fix tests
2024-03-10 19:20:11 +01:00
Arnau Mora
66f0075cc9 Rewrite AddWebdavMountActivity to Compose (#630)
* Migrated AddWebdavMountActivity to Compose

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Minor changes, use PasswordTextField

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-03-10 18:33:22 +01:00
Ricki Hirner
3edcc02a21 Minor classes refactoring 2024-03-09 17:42:38 +01:00
Ricki Hirner
77ab1801fa Move Hilt SyncComponent 2024-03-09 16:29:55 +01:00
Ricki Hirner
d9ddfafbf9 IntroActivity: get IntroPageFactory over Hilt 2024-03-09 14:37:33 +01:00
Ricki Hirner
c5adc93d1a Bump version to 4.3.15-alpha.1 2024-03-09 14:27:37 +01:00
Ricki Hirner
42f99e644d Rewrite intro pages to Compose (dropping all Fragments) (#626) 2024-03-09 14:25:24 +01:00
Arnau Mora
1cbfedc9e4 Rewrite PermissionsActivity, PermissionsFragment to Compose (#583)
* Migrated `PermissionsFragment` to Compose

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Migrated `PermissionsActivity` to Compose

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed preview

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed TODO

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Don't show unavailable permissions, explicitly pass Activity model, minor changes

* Added status text

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Increased margins

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Button uppercase, safeguard Keep Permissions launch

* Moved tasks app availability to viewmodel

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added tasks watcher back

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed unnecessary livedata

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Cleanup

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-03-08 16:35:29 +01:00
Ricki Hirner
962dab7cf2 Rewrite AccountActivity to Compose (#617)
* [WIP] Add AccountActivity2 in Compose

* Make paging collections work when data changes

* [WIP] Add ProgressIndicator TODO

* [WIP] CardDAV: add swipe-to-refresh

* [WIP] Correctly use Pager

* [WIP] Only show Webcal tab when there are subscriptions

* [WIP] Implement collection properties dialog

* Implement "create collection" and "show only personal collection"

* [WIP] Add collection overflow menu items

* Show color as left border, max. 2 icons per row

* [WIP] Delete collection dialog

* Add "delete collection"

* Implement "Force read-only"

* Delete old XML classes and resources

* Add permissions warning

* Implement "Rename account"

* Case-insensitive sorting, minor changes

* Horizontal arrangement

* Less integration of Webcal subscriptions (other layout)

* Accessibility

* Collection list: provide ID als key for lazy list

* Only show "Create addressbook/calendar" when there's at least one writable homeset
2024-03-08 16:32:55 +01:00
Arnau Mora
2e669812b1 Rewrite WebdavMountsActivity to Compose (#607)
* Migrated to Compose

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Text hides when there are mounts

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed todo

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Migrated to Compose

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Text hides when there are mounts

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed todo

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed vertical scroll

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added action for ClickableText

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed indentation

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Changed layout

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed overflow preferences

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed padding

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Changed link color

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed preview

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed back arrow

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Require explicit Context for helpUrl to make it work in Compose preview

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-03-08 16:29:16 +01:00
Ricki Hirner
7e743f2dbd Move SyncComponent to syncadapter package 2024-03-05 16:09:55 +01:00
Ricki Hirner
6f08901f04 Add permissions for CI Github release action to allow creating a discussion 2024-03-04 17:04:11 +01:00
Ricki Hirner
f06eef2e72 Fetch translations from Transifex 2024-03-04 14:19:47 +01:00
Ricki Hirner
f06d396dd8 Bump version to 4.3.14 2024-03-04 14:16:50 +01:00
Ricki Hirner
3004bf14bc Update dependencies 2024-03-04 14:16:40 +01:00
Ricki Hirner
b511c04493 Bump version to 4.3.14-beta.2 2024-03-02 00:00:59 +01:00
Ricki Hirner
1337c8d648 Update ical4android 2024-03-02 00:00:18 +01:00
Ricki Hirner
e3485ec3ec Use gradle-managed device for testing (#609) 2024-03-01 20:15:45 +01:00
Ricki Hirner
dc1c77c336 Update APG and dependencies 2024-03-01 11:49:59 +01:00
Ricki Hirner
6b9395c254 Adapt WelcomeFragment for smaller devices (#606) 2024-02-29 00:18:58 +01:00
Arnau Mora
e24543a298 Rewrite WelcomeFragment to Compose (#582)
* Migrated to Jetpack Compose

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Simplify layout, remove animations

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-02-28 17:35:14 +01:00
Ricki Hirner
334fdb5953 Disable periodic sync workers that don't exist anymore (#602)
* Disable old periodic worker when renaming accounts

* Periodic sync worker now disables itself when an account is not available anymore

* Add test
2024-02-28 13:38:45 +01:00
Sunik Kupfer
86742f5b18 Don't upload event when calendar is read only (#587)
* Make readOnly a LocalCollection property

* Move readOnly detection to SyncManager

* Add readOnly state access to LocalCalendar

* Add not implemented error to readOnly state access of LocalJtxCollection

* Handle read-only state of calendar at dirty events upload

* Handle read-only state of calendar at processing of locally deleted events

* Remove todo and update kdoc

* Fix indenting

* Add read-only prop to LocalTestCollection

* Add read-only state access to LocalTaskList

* LocalTestCollection: don't set read-only

* Update ical4android (for new KDoc)

* Make LocalCollection readOnly-API read only and take value from content provider during populate()

* SyncManager: use readOnly direct from localCollection

* Lift resetDeleted up to LocalResource

* Override and use resetDeleted for LocalEvent

* Add resetDeleted to LocalJtxICalObject

* Add resetDeleted to LocalTask

* Add resetDeleted to LocalTask

* Add resetDeleted to LocalTestResource

* Provide default access level

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-02-27 13:00:40 +01:00
Arnau Mora
df2b7d2bd0 Fixed span styles for URL annotations (#598)
* Fixed span styles

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Pushing initial style to builder

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-02-26 15:44:19 +01:00
Ricki Hirner
4c1d9d21bd Releases: generate release notes and discussion thread automatially (#596) 2024-02-26 12:41:04 +01:00
Arnau Mora
be309e15b3 Rewrite BatteryOptimizationsFragment to Compose (#580)
* Migrated to Jetpack Compose

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added `observeBoolean`

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Simplified settings interaction

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Migrated to Jetpack Compose

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added `observeBoolean`

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Simplified settings interaction

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Use SafeAndroidUriHandler instead of UiUtils.launchUri

* Removed animation for manufacturerWarning

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed animation for manufacturerWarning

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added `getBooleanLive`

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Using `getBooleanLive`

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Moved UI definitions to file scope

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Don't use specific times for waiting in tests

* Renamed function

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* More exact naming

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-02-23 14:12:02 +01:00
Ricki Hirner
6b1367d6dc Reflect ical4android tasks API changes 2024-02-22 12:16:10 +01:00
Ricki Hirner
774fced9e8 Fetch translations from Transifex 2024-02-22 12:07:32 +01:00
Ricki Hirner
dd9681e75a Bump version to 4.3.14-beta.1 2024-02-22 12:06:23 +01:00
Ricki Hirner
bc3dbf09fc Update dependencies 2024-02-22 12:04:51 +01:00
Ricki Hirner
cae1ed5efb More correct usage of expedited workers (#566)
* Add foreground notification type to expedited workers (required for Android 14)

* Make SyncWorker a long-running worker

* Don't use expedited SyncWorker for everything; handle foreground service launch restriction

* AddressBookSyncer: only request expedited for sub-jobs when parent job is expedited, too

* RefreshCollectionsWorker is not long-running -> no foreground service type needed

* Fix tests

* Don't use foreground service type in ForegroundInfo

* Make SyncWorker not long-running
2024-02-22 11:56:44 +01:00
Ricki Hirner
e5cf7610ad OpenSourceFragment: use correct string resource 2024-02-21 16:47:56 +01:00
Arnau Mora
0253cd3d89 Migrate to Kotlin DSL (#586)
* Migrated to Kotlin DSL

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Migrated to Kotlin DSL

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Update versions, suppress nofications for libs we don't want to upgrade

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-02-20 11:48:02 +01:00
Ricki Hirner
30bb981975 Don't filter translators by role (but exclude "bitfire" user) (bitfireAT/davx5#532) 2024-02-20 10:00:03 +01:00
Arnau Mora
f6ac4e02d6 Rewrite OpenSourceFragment to Compose (#581)
* Migrated to Jetpack Compose

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Removed layout file

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Adapt paddings and font size

* Added check for non-available browsers

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Add SafeAndroidUriHandler

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-02-19 19:09:49 +01:00
Ricki Hirner
af00ff0c4c Update dependencies 2024-02-16 11:30:08 +01:00
Arnau Mora
8ac95e0796 Use gradle version catalog (#564)
* Using version catalog

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Fixed library names

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Remove lifecycle-ext

* Rename, reorder

* Remove unnecessary entries

* Use BOM for Compose again

* Try to fix BOM

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-02-15 11:21:09 +01:00
Ricki Hirner
5a5023bf54 Bump version to 4.3.13.1 2024-02-14 17:09:50 +01:00
Ricki Hirner
ea6e520c93 Drop foreground service (#569)
* Remove foreground service permissions

* Remove foreground service + setting

* Remove unused setting name and notification IDs
2024-02-14 17:05:53 +01:00
Arnau Mora
a7f6192177 Rewrite CreateAddressBookActivity to Compose (#559)
* Added initial layout

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Improved UI

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Replaced autofill with radio buttons

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Got rid of unnecessary errors

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Got rid of null indicators

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added default home set selection

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Minor UI changes

* Drop displayNameError

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-02-12 13:28:11 +01:00
Sunik Kupfer
3dd63df5c8 Rewrite RenameAccountFragment to compose (closes #478) (#561)
* Rewrite RenameAccountFragment to compose

* Add padding to text field, disable RENAME button when old name = new name

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-02-12 11:40:27 +01:00
Ricki Hirner
ba084255c0 Fetch translations from Transifex 2024-02-11 20:47:26 +01:00
Ricki Hirner
847d452884 Bump version to 4.3.13 2024-02-11 20:46:21 +01:00
Ricki Hirner
b2d67a5dfb Version bump to 4.3.13-rc.1 2024-02-07 21:08:01 +01:00
Ricki Hirner
94226aac1f Update dependencies, CI actions, test emulator API level 2024-02-07 11:01:34 +01:00
Ricki Hirner
8ce6fbe776 Update vcard4android (don't write structured name when FN = ORG) 2024-02-06 21:10:38 +01:00
Ricki Hirner
55c499fbe9 prepareForUpload: use UID from data class instead of querying it explicitly from content provider (#555) 2024-02-06 14:22:29 +01:00
Ricki Hirner
53a446bcf9 [CI] Update Github actions 2024-02-02 13:50:18 +01:00
Ricki Hirner
4ee6bfe276 Update dependencies (including ical4android) 2024-02-01 19:53:04 +01:00
Ricki Hirner
6817c17686 Nextcloud Login Flow: use Saved State for low memory conditions (#553) 2024-01-31 17:12:41 +01:00
Ricki Hirner
d108ea8a7b Use ksp (instead of kapt) for Hilt (bitfireAT/davx5#521) 2024-01-31 14:03:56 +01:00
Ricki Hirner
d2de737857 Version bump to 4.3.13-beta.2 2024-01-31 13:40:27 +01:00
Ricki Hirner
37e0605f9e Fetch translations from Transifex 2024-01-31 13:17:59 +01:00
Ricki Hirner
95d098541f Lint (#551)
* Lint

* Optimize imports

* Icon
2024-01-31 13:09:15 +01:00
Ricki Hirner
b2d06a491d Fix Nextcloud Login Flow "IllegalStateException: OffsetMapping.originalToTransformed" (#550)
* Move State out of Composable

* Disable text field instead of making it read-only during progress
2024-01-31 12:46:08 +01:00
Ricki Hirner
09b15c1e75 Android 14 Compatibility (#545)
* Increase target SDK level to 34

* ForegroundService: specify "data sync" foreground service type
2024-01-31 12:09:10 +01:00
Arnau Mora
a9a699c5b9 Moved to gradle/actions/setup-gradle@v3 (#549)
Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2024-01-30 17:12:32 +01:00
Ricki Hirner
dd036b91fc Add battery saver warning (#542)
* Show battery saver warning in account list and debug info

* Move app warnings to model class

* Debug info: more verbose text

* Restore previous strings for sync enqueued/started
2024-01-30 15:50:03 +01:00
Ricki Hirner
fbed5c7d67 WebDAV performance optimizations (#543)
* Make ranged GET requests cancellable; reduce notification update frequency

* Include original exception as a cause in WebDAV ErrnoException

* Add KDoc for threading
2024-01-30 11:55:59 +01:00
Ricki Hirner
f0eb140777 Update dependencies (including ical4android) 2024-01-26 13:05:25 +01:00
Patrick Lang
dcba94ac70 jtxBoard: added localCollection.updateLastSync() in post-processing (bitfireAT/davx5#505)
* added localCollection.updateLastSync() in post-processing

* Update build.gradle

updated ical4android version

* Update build.gradle

* Update build.gradle

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-01-25 15:46:58 +01:00
Arnau Mora
202a91a7a4 LocalEvent: synchronize UID handling with ical4android (#541)
* Got rid of uid writes

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Using new ical4android version

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Changed commit id

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Updated ical4android

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2024-01-24 19:43:38 +01:00
Ricki Hirner
a313ace66d Fetch translations from Transifex 2024-01-20 13:42:05 +01:00
Ricki Hirner
bdbab77f5e Version bump to 4.3.13-beta.1 2024-01-20 13:40:53 +01:00
Arnau Mora
88e41d1eed Update dav4jvm and other dependencies (bitfireAT/davx5#518)
* Upgrade Compose Compiler to 1.5.8

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Upgrade Kotlin to 1.9.22

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Upgrade KSP to 1.0.17

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Upgrade dav4jvm 93dddcd

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Upgrade ViewModel to 2.7.0

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Updated imports

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Updated imports

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Updated imports

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2024-01-20 12:40:38 +01:00
Sunik Kupfer
ea5fc54003 Validate email before starting google oauth flow (#535)
* Validate email before starting auth flow (see #525, fixes #534)

* Simplify expression (minor change)

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-01-18 13:11:08 +01:00
Arnau Mora
0c834a5c42 Tasks intro page: "I don't need tasks" checkbox not visible in landscape mode (#530)
* Added optional modifier

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Fixed model argument name

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added bottom padding for bottom bar

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2024-01-16 23:11:39 +01:00
Arnau Mora
2ac81b6342 Darkened colors of accounts screen background image for dark mode (#529)
* Moved all colors to resources

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Darkened colors for night mode

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2024-01-15 09:45:46 +01:00
Ricki Hirner
46a8f0f205 Add Dependent issues workflow 2024-01-11 17:35:17 +01:00
Arnau Mora
b8f4b9af30 Migrate DebugInfoActivity to Compose (#509)
* Gave more flexibility

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Migrated to Compose

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Adjusted paddings

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added missing observer

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* CardWithImagE: add another preview with subtitle, icon and content

* Made buttons uppercase

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Adjusted spacings

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Changed snackbar host state

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Changed nullable expression

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Using shareFile for zip

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Minor changes (comments/formatting)

* Switched to view instead of sharing files

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Adapted image height for landscape

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* CardView: allow to pass image alignment; use card_theme_max_height

* DebugInfoActivity: paddings, images

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-01-11 17:31:08 +01:00
Ricki Hirner
3c4601f7a1 WebDAV memory cache: store values using SoftReference (bitfireAT/davx5#515)
so that the values can be free if there's not enough memory
2024-01-11 13:48:18 +01:00
Ricki Hirner
357cf09be7 WebDAV: refactor paging and caches (bitfireAT/davx5#502)
* WebDAV: don't try to load 0-byte segments

* Extracted segmentation logic to SegmentedReader for testability

* Reorganize and simplify caches

* Refactor thumbnail cache

* Use coroutines with Dispatchers.IO instead of custom Executor

* Remove obsolete classes

* Fix tests, simplify DiskCache

* Paging reader: cache current page

* PagingReader tests

* Thumbnails: timeout for generation and not only for waiting

* openDocumentThumbnail: actually cancel HTTP request when method is cancelled

* Better KDoc

* Add further tests
2024-01-10 14:59:57 +01:00
darealdemayo
6ce0d35e6d Add purelymail.com (#521) 2024-01-10 14:28:52 +01:00
Sunik Kupfer
24401cc990 Fix ProGuard issue with javax.xml.namespace.QName; update AGP (bitfireAT/davx5#512)
* Update to AGP 8.2.1
* Add keep rule for javax.xml.namespace.QName

See #511
Closes bitfireAT/davx5#499
2024-01-08 15:00:19 +01:00
Ricki Hirner
cf0c3040fc Run uploadDirty() regardless of processLocallyDeleted() result (bitfireAT/davx5#510)
Run uploadDirty() regardless of whether processLocallyDeleted() did something or not
2024-01-05 11:16:50 +01:00
Arnau Mora
6b660c2be6 Rename LoginCredentialsFragmentFactory to LoginFragmentFactory (bitfireAT/davx5#507)
* Renamed LoginCredentialsFragmentFactory to LoginFragmentFactory

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Fixed fragment name

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Rename LoginInitFragment (managed) to ManagedLoginInitFragment

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-01-05 11:16:47 +01:00
Sunik Kupfer
20652b9325 NextcloudLoginFlowFragment: minor changes (sync bitfireAT/davx5#501)
---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2024-01-03 11:25:32 +01:00
Ricki Hirner
e5c73bead5 Downgrade AGP to 8.1.4 (fixes bitfireAT/davx5-ose#511) 2023-12-25 15:51:25 +01:00
Ricki Hirner
581622272c Bump version to 4.3.12.1 2023-12-25 15:51:18 +01:00
Ricki Hirner
320fe1dfd8 Compile releases without build and configuration cache 2023-12-25 15:50:54 +01:00
Arnau Mora
09253a2454 Upgrade Compose Compiler to 1.5.7 (#510)
Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2023-12-25 11:48:09 +01:00
Ricki Hirner
895f0a1541 Fetch translations from Transifex 2023-12-23 12:40:16 +01:00
Ricki Hirner
05360e818a Version bump to 4.3.12 2023-12-23 12:34:06 +01:00
Ricki Hirner
5d7dea0ebf Nextcloud Login flow: provide default DAV_PATH (/remote.php/dav) (#497) 2023-12-23 12:34:00 +01:00
Ricki Hirner
88238a2406 ConcurrentUtilsTest: fix testRunSingle_SameKey_Parallel (bitfireAT/davx5#494)
ConcurrentUtilsTest: changed testRunSingle_SameKey_Parallel to testRunSingle_SameKey_Nested
2023-12-20 14:34:53 +01:00
Ricki Hirner
357275fe83 Version bump to 4.3.12-rc.1 2023-12-20 14:02:08 +01:00
Ricki Hirner
e2ac368dbc Update dependencies 2023-12-20 14:02:08 +01:00
Sunik Kupfer
532a143cc8 Add PasswordTextField Composable (taken from bitfireAT/davx5#437)
---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-12-20 13:55:13 +01:00
Arnau Mora
c087834452 Rewrite TasksFragment to Compose (#481)
* Added `CardWithImage`

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added `RadioWithSwitch`

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Migrating to Compose

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added observers

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Fixed functions signature

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added kdoc

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Removed layout

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Color for disabled

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added "don't show" behaviour

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added all tasks providers

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Moved checkbox to correct location

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Fixed don't need behaviour

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added theme

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added todo

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added support for link annotations

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added support for annotated strings and urls

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added tests for HTML annotation

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Extracted `linkStyle`

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Removed observers for requested

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Removed more observers

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added multiple links test

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Moved `installApp` to `TasksCard`

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Moved all model calls to composable

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Removed preview since not usable

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Got rid of TasksFragment

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Fixed import

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Switched link color to orange

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added missing copyright information

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Use HtmlCompat and existing Spanned.toAnnotatedString

* Added default content

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Renamed image content description

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Got rid of empty content

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Made summary of RadioWithSwitch composable

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added missing entry point annotation

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added click handling for tasks org

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Got rid of the preview provider

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Minor changes

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-12-15 13:09:42 +01:00
Ricki Hirner
da5b765b3a WebdavMountsActivity: use ShareCompat.IntentBuilder (bitfireAT/davx5#490)
WebdavMountsActivity: use ShareCompat.IntentBuilder to create sharing intent
2023-12-15 12:23:00 +01:00
Ricki Hirner
a937442a82 Update ical4android (updates ical4j to 3.2.14 and hopefully fixes bitfireAT/davx5#398) (bitfireAT/davx5#489) 2023-12-12 14:52:36 +01:00
Ricki Hirner
c56461ea9e WebDAV: allow other MIME types for (Ranged) GET, use coroutines for streaming (#503)
* StreamingFileDescriptor: use coroutines instead of threading

* WebDAV GET: accept any MIME type, but prefer known one
2023-12-12 14:39:43 +01:00
Ricki Hirner
769147b193 Use dav4jvm version that alway appends a trailing slash to MKCOL calls (#504) 2023-12-11 15:34:01 +01:00
Arnau Mora
8a0e1151ec Rewrite ExceptionInfoFragment into Compose (#485)
* Migrated dialog to Compose

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added extended compose icons

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2023-12-09 12:24:36 +01:00
Ricki Hirner
f2f40049b8 Update Kotlin, dependencies, Gradle 2023-12-09 11:56:00 +01:00
Ricki Hirner
7a259383be Update dependencies, GoogleLoginFragment: use clickableText instead of legacy TextView 2023-12-06 13:14:32 +01:00
Sunik Kupfer
c3e980cabf Sync add-account button code with bitfireAT/davx5#481
---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-12-05 16:07:01 +01:00
Ricki Hirner
d6861db8a6 Comment out testRefreshCollections_enqueuesWorker (bitfireAT/davx5#487) 2023-12-05 16:03:53 +01:00
Ricki Hirner
1854822757 RefreshCollectionsWorkerTest: wait for completion of enqueue (bitfireAT/davx5#486)
* RefreshCollectionsWorkerTest: wait for completion of enqueue

* Work may have finished already
2023-12-04 18:19:21 +01:00
Sunik Kupfer
787e1a687f Set METHOD_ALERT on default event reminders (#493) 2023-12-03 21:12:56 +01:00
Ricki Hirner
f8b1cd5b3c Correctly save app/build and configuration cache for dev branch builds 2023-12-03 16:59:11 +01:00
Ricki Hirner
fb7658cfaa AboutLibraries: explicitly export library definitions at release (#485)
Export library definitions for release
2023-12-03 16:20:35 +01:00
Ricki Hirner
3eb8eba70c Update AGP and dependencies 2023-12-03 16:15:57 +01:00
Ricki Hirner
fca6b5b890 Fetch translations from Transifex 2023-11-20 14:30:05 +01:00
Ricki Hirner
93cad92876 Version bump to 4.3.11 2023-11-20 14:29:15 +01:00
Ricki Hirner
8ddd3d66f0 Version bump to 4.3.11-alpha.2, CI: hopefully speed up release workflow 2023-11-20 14:28:41 +01:00
Ricki Hirner
a27a4fc7ae vcard4android: sync birthdays with time offset (#488) 2023-11-18 15:05:29 +01:00
Ricki Hirner
127511576b Update AGP to 8.1.4 2023-11-18 14:56:53 +01:00
Ricki Hirner
90c72ec013 Cache app/build to speed up builds 2023-11-17 16:12:05 +01:00
Ricki Hirner
ee637e4f6d ForegroundService: don't use stopSelf or stopForeground (bitfireAT/davx5#462)
May fix #342
2023-11-16 13:21:34 +01:00
Michael Biebl
769825b402 AboutActivity: Add back no warranty disclaimer (#477)
This was dropped during the Compose rewrite.
Also align the copyright information as it looks better.
2023-11-15 17:39:41 +01:00
Ricki Hirner
a792d4db98 Version bump to 4.3.10 2023-11-14 13:11:48 +01:00
Ricki Hirner
ef723aa555 Fetch translations from Transifex 2023-11-14 13:11:36 +01:00
Ricki Hirner
f7fc82b801 Don't allow app name translation 2023-11-14 11:59:44 +01:00
Arnau Mora
59dc681fe4 AccountsActivity: disable sync button when there are no accounts (#473)
Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2023-11-14 10:49:02 +01:00
Ricki Hirner
d22da0d230 Fetch translations from Transifex 2023-11-13 16:32:45 +01:00
Ricki Hirner
6829b3259f Bump version to 4.3.10-rc.1 2023-11-13 16:31:01 +01:00
Ricki Hirner
2f761facc9 AccountSettings: don't throw IllegalArgumentException when reading/writing sync interval (bitfireAT/davx5#456) 2023-11-13 15:07:51 +01:00
Ricki Hirner
eeffbdcf6d Go back to dnsjava 2.1.9 to avoid crashes on Android 7 (bitfireAT/davx5#454) 2023-11-13 14:32:34 +01:00
Ricki Hirner
2a9c27d4f7 AboutActivity: remove pager padding 2023-11-13 14:32:32 +01:00
Ricki Hirner
72f0579f41 Update dependencies 2023-11-13 14:32:29 +01:00
Ricki Hirner
449e886d49 AccountsActivity: center background image (fixes bitfireAT/davx5#434) 2023-11-13 10:30:46 +01:00
Arnau Mora
6055749e42 Fixed horizontal scroll (bitfireAT/davx5#447)
* Fixed sizing

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Small changes

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-11-12 11:34:50 +01:00
Ricki Hirner
0c1e4fd3cb Version bump to 4.3.10-beta.1 2023-11-09 17:02:32 +01:00
Ricki Hirner
61c1ef8831 HTTP Workers: use runInterruptible instead of interrupting manually (bitfireAT/davx5#444)
* RefreshCollectionsWorker: use runInterruptible instead of interrupting manually

* SyncWorker: use CoroutineWorker + runInterruptible

* Use global SyncWorkDispatcher that guarantees classLoader to be set

* Set SyncWorkDispatcher for whole SyncWorker's doWork

* Remove obsolete test

* SyncManager: add structured concurrency again

* Use up to <number of processors> threads for synchronization

---------

Co-authored-by: Sunik Kupfer <kupfer@bitfire.at>
2023-11-09 17:02:27 +01:00
Ricki Hirner
42bd1e8449 Don't cancel service detection when DetectConfigurationFragment's view model is cleared (bitfireAT/davx5#442)
- Rewrite DetectConfigurationFragment to Compose
- Use coroutines and runInterruptible instead of Thread
- Only cancel service detection when back is pressed
2023-11-09 17:02:14 +01:00
Ricki Hirner
46615a8337 Version bump to 4.3.10-alpha.1 2023-11-09 17:02:12 +01:00
Ricki Hirner
518e5147fe Rewrite AccountsActivity to Compose (bitfireAT/davx5#431)
---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Sunik Kupfer <kupfer@bitfire.at>
2023-11-07 17:22:44 +01:00
Ricki Hirner
9d739dd087 Clean up Hilt modules 2023-11-07 15:03:07 +01:00
Ricki Hirner
4a200dfbb7 Rewrite About activity to Compose (bitfireAT/davx5#432) 2023-11-07 14:52:42 +01:00
Ricki Hirner
cf9340107f RandomAccessCallback.Wrapper: support multiple state machine instances at same time (bitfireAT/davx5#428)
* RandomAccessCallback.Wrapper: support multiple state machine instances at same time

- support multiple state machine instances at same time
- provide explicit Exception/error code when the remote server doesn't support ranged requests

* Only use RandomAccessCallback when server explicitly advertises range requests

---------

Co-authored-by: Arnau Mora <arnyminerz@proton.me>
2023-11-07 14:11:22 +01:00
Sunik Kupfer
fbe0c4451b Add UiUtils method to render Spannables (from HtmlCompat) with Compose (bitfireAT/davx5#430)
* Add Spanned.toAnnotatedString, minor changes

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-11-01 12:00:14 +01:00
Arnau Mora
ca26155eed jtxBoard collections: honor "Manage calendar colors" account setting (bitfireAT/davx5#427)
Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2023-10-31 16:11:55 +01:00
Ricki Hirner
1a36ee2d60 Fetch translations from Transifex 2023-10-26 13:15:04 +02:00
Ricki Hirner
046bacff3f Version bump to 4.3.9 2023-10-26 12:26:17 +02:00
Ricki Hirner
73475640f7 Google Login: minor UI improvements (bitfireAT/davx5#416)
- automatically append @gmail.com
- show Go IME action for login and client ID
2023-10-26 12:26:13 +02:00
Sunik Kupfer
1e6a457a0d Fix related google calendars not being found (bitfireAT/davx5#409)
* Minor changes
- update kdoc
- rename method and variables

* Add proxy parents to related resource detection

* Rename argument, query ResourceType

* Remove unnecessary utility method

* Change parentOf to extension function; Always return URL with trailing slash

* Use calendar-proxy-read/write ResourceType from new dav4jvm

* Use max. two levels of recursion to detect shared Google calendars

* Revise test and adapt method

* Simplify HttpUrl.parent()

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-10-26 12:26:09 +02:00
Ricki Hirner
0215e98326 Version bump to 4.3.9-beta.2 2023-10-24 21:22:26 +02:00
Ricki Hirner
9dd8290004 Debug info: show periodicity and next run of sync workers (bitfireAT/davx5#415) 2023-10-24 21:22:22 +02:00
Ricki Hirner
8263b5fcf8 WorkManager: add stop reason to debug info and sync logs (bitfireAT/davx5#413) 2023-10-23 16:58:52 +02:00
Arnau Mora
fe679da03b Refactor HiltViewModels ApplicationContext (#446)
* Simplified ViewModels

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Fixed injection

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Fixed settings injection

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added missing import

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Fixed application

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Fixed constructors and got rid of utils

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Optimized imports

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added missing annotation

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2023-10-19 17:36:11 +02:00
Ricki Hirner
f8330e8f52 dnsjava: fix R8 rules 2023-10-18 15:31:01 +02:00
Ricki Hirner
c451c3fd70 Version bump to 4.3.9-beta.1 2023-10-18 15:20:35 +02:00
Ricki Hirner
e41ac428c9 WebcalFragment: remove unused menu item code 2023-10-18 15:19:23 +02:00
Ricki Hirner
b26ae345cd Nextcloud: pre-select contact group method (CATEGORIES) (bitfireAT/davx5#410)
* LoginActivity: refactor menu to MenuProvider; LoginModel: add contact group type

* Take LoginModel group method into account when creating the account; Nextcloud login: set preferred contact group type
2023-10-18 15:18:46 +02:00
Ricki Hirner
52747e632f LoginActivity: add Nextcloud Login Flow (bitfireAT/davx5#403)
* Replace onActivityResult by contract

* Add Nextcloud option to default login screen

* Decouple NextcloudLoginFlowComposable from model

* UI and model changes

* Single-line URL field

* Add progress indicator and other secondary UI
2023-10-18 15:17:53 +02:00
Ricki Hirner
58d4a9f663 Make all IntroFragments appear at first start (#452)
* IntroFragments: use (factory,order) List instead of (order,factory) Map to store them

* Adapt OpenSourceFragment order
2023-10-17 18:50:42 +02:00
Ricki Hirner
8ffed42eb9 PermissionsIntroFragment: take jtx Board and tasks.org permissions into account (#450) 2023-10-17 10:17:17 +02:00
Arnau Mora
5ae70cb5d0 BatteryOptimizationIntroFragment: use contract instead of onActivityResult (#444)
* Using result launcher

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Minor re-ordering

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-10-16 15:31:17 +02:00
Arnau Mora
599c905610 Replace deprecated menu overrides (#443)
* Migrated to menu provider

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Removed override

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Cleanup

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Fixed menus

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Minor changes

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-10-16 15:30:26 +02:00
Ricki Hirner
c8cd6d780c [WebDAV] Add timeout for RandomAccessCallback notification (bitfireAT/davx5#408)
* [WIP] Add timeout for RandomAccessCallback

* Use state machine to handle timeout

* Use sealed class for states, guard callback access with correct states
2023-10-16 11:10:46 +02:00
Arnau Mora
4ce6fcbf44 Replaced all onBackPressed usages (bitfireAT/davx5#406)
* Replaced all `onBackPressed` usages

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added missing finish

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added more finish statements

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2023-10-10 18:43:44 +02:00
Sunik Kupfer
3da48ab3a2 362 increase minimum api level to android 7 (bitfireAT/davx5#363)
* Increase minSdkVersion to 24 (Android 7.0)

* Remove obsolete api level checks

* Use latest dnsjava

* Use latest apache commons

* Minor formatting

* Unify getSystemService() calls

* Remove further unnecessary calls

* Remove noinspection GradleDependency for Apache Commons libs

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-10-10 18:43:24 +02:00
Arnau Mora
0ba00d7bb0 Migrated startActivityForResult (bitfireAT/davx5#407)
Migrated activity result

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2023-10-10 18:42:20 +02:00
Sunik Kupfer
6d30ef42e4 Foreground service: startForeground() within 5 seconds to avoid exception (bitfireAT/davx5#405)
* Call startForeground in onCreate

* Add stopSelf() and comments

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-10-10 10:18:22 +02:00
Ricki Hirner
088136ded7 Version bump to 4.3.8 2023-10-09 12:58:09 +02:00
Ricki Hirner
caf04c4c45 Fetch translations from Transifex 2023-10-09 12:40:35 +02:00
Sunik Kupfer
81fbe2b7a6 Use worker names as work tags (bitfireAT/davx5#404) 2023-10-09 12:38:43 +02:00
Ricki Hirner
437fb30a94 Rename java sources directories to kotlin 2023-10-07 10:22:08 +02:00
Ricki Hirner
e85727e869 Update dependencies 2023-10-07 10:18:53 +02:00
Ricki Hirner
f57cd77ced Update dependencies, bump version to 4.3.8-alpha.1 2023-10-06 12:58:46 +02:00
Sunik Kupfer
6bbdcb332f Include address book account syncs, when querying sync status (bitfireAT/davx5#378)
* Use tags instead of uniqueWorkNames for work queries.

* Also include address book accounts, when querying sync status.

Give address book account sync workers their parent (main account) sync workers tag too, such that they will be included at the query for sync status of their parent account.

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-10-06 12:52:42 +02:00
Arnau Mora
6db1473e00 Update ical4android (#425)
Updates ical4j to 3.2.13, which should fix the backslash issue
2023-09-27 13:55:47 +02:00
Ricki Hirner
fd4872adb5 Minor connectivity/VPN UI adaptions (bitfireAT/davx5#391)
* Low storage: don't show notification anymore, adapt info message

- The impact of low storage is not as critical anymore that a notification is required.
- Info message adapted

* Update "account settings: ignore VPN" strings

* Update "No internet" string
2023-09-27 13:54:48 +02:00
Ricki Hirner
ba310762f9 Repair beta feedback (bitfireAT/davx5#394)
- use Google Play In-App Review API for private feedback on -gplay (fall back to email)
- start email intent again when "beta feedback" is selected in navigation drawer
2023-09-27 13:54:41 +02:00
Ricki Hirner
02c401b4d5 AccountActivity: animate progress bar becoming invisible (bitfireAT/davx5#366)
* [WIP] Animate invisibility

* Reordered for cleaner look

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Fixed animation

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Reduced animation time for going to visible to 0

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Remove logging

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Arnau Mora <arnyminerz@proton.me>
2023-09-22 17:38:27 +02:00
Arnau Mora
48327e44f5 AccountActivity: make content underneath FABs accessible (bitfireAT/davx5#390)
* Doubled bottom padding

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Account activity: increase margin for two FABs even more

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-09-22 17:38:21 +02:00
Ricki Hirner
4faecc653f Fetch translations from Transifex 2023-09-22 15:57:01 +02:00
Ricki Hirner
7be6e0636b Bump version to 4.3.7 2023-09-22 15:53:00 +02:00
Ricki Hirner
b8e4ba62f5 Update dependencies, bump version to 4.3.7-rc.1 2023-09-21 18:53:36 +02:00
Arnau Mora
ead6293be6 cert4android: certificates are not saved across app restarts; other bug fixes (bitfireAT/davx5#392)
* Updated cert4android dependency

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-09-21 18:53:27 +02:00
Arnau Mora
651ab9c53c Fixed empty translators.json (#401)
* Update translators.json

* Added JSON parsing try-catch

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Log complete exception

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-09-12 13:03:46 +02:00
Ricki Hirner
bc7a320916 Don't start multiple tests for PRs 2023-09-12 13:03:15 +02:00
Sunik Kupfer
464ba7d76e Tests for ConnectionUtils (bitfireAT/davx5#372)
* Add tests for internetAvailable()

* Add tests for wifiAvailable()

* Add TODO for test case

* Add tests for internetAvailable() covering multiple network connections

* Minor KDoc

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-09-11 11:41:41 +02:00
Angus Gratton
52e26e34d5 DavDocumentsProvider: Log a warning for responses of type OTHER. (#400)
If a WebDAV server is misconfigured (for example, behind an HTTP proxy that
strips a URL prefix and doesn't correctly forward this to the WebDAV server)
then PROPFIND can return resources whose URLs don't match the request URL. These
are resolved by dav4jvm as HrefRelation.OTHER.

Currently this situation produces no output at all in DAVx5 (logs or app) and
the WebDAV share appears accessible but empty. It is possible to create files in
the share, but not to see them again afterwards!

Of course a misconfigured server isn't the WebDAV client's responsibility to
resolve, but adding a warning in the log provides an extra clue for anyone
trying to debug it.
2023-09-08 13:48:39 +02:00
Ricki Hirner
0c991288d5 Bump version to 4.3.6.1 2023-09-07 00:29:39 +02:00
Ricki Hirner
f1a1d0efd8 Fix bug where sync isn't possible as soon there's any connection without INTERNET (bitfireAT/davx5#370)
Fix bug where sync wasn't possible as soon there's any connection without INTERNET (bitfireAT/davx5#369)
2023-09-07 00:29:23 +02:00
Ricki Hirner
56deab70ee Version bump to 4.3.6 2023-09-06 16:50:09 +02:00
Sunik Kupfer
5640250359 Wait an appropriate delay before SyncWorker retries after soft errors (bitfireAT/davx5#337)
* Use appropriate delayUntil value for retrying syncs on 503s.

Crop server suggested retryAfter value to self defined min/max values and use a reasonable default value if non-existent.

* Add tests for getDelayUntil

* Wait appropriate delay, before retrying sync after a soft error happened

* Increase max and default sync delays after soft errors

* Increase initial backoff time for SyncWorker retries

* Minor getDelayUntil changes

* Minor changes

- store delayUntil in seconds
- pass duration instead of timestamp to Thread.sleep
- other minor changes

* Use Instant instead of Long timestamps

* Correct calculation of blocking duration

* Indicate soft error occurred on 503 server message

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-09-05 20:30:52 +02:00
Ricki Hirner
b670979f12 Fetch translations from Transifex 2023-09-05 20:30:30 +02:00
Ricki Hirner
251cf1b2e9 Update dependencies (including vcard4android), bump version to 4.3.6-rc.1 2023-09-05 10:42:33 +02:00
Ricki Hirner
4ad54cd28b Update dependencies (including cert4android and vcard4android) (bitfireAT/davx5#360)
* Update dependencies (including cert4android and vcard4android)

* Migrated to new version of ical4android

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Increased compileSdk

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Upgraded browser

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Only use `appInForeground` for `customCertsUi`

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Removed unnecessary variable and fixed trust manager

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Cleaned up trust manager factory

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Update dependencies (including cert4android and vcard4android)

* Migrated to new version of ical4android

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Increased compileSdk

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Upgraded browser

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Only use `appInForeground` for `customCertsUi`

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Removed unnecessary variable and fixed trust manager

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Cleaned up trust manager factory

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Minor changes

* Fixed build for SDK 34

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Migrated certificate trusting

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* NetworkConfigProvider: handle invalid trusted certificate

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Arnau Mora <arnyminerz@proton.me>
2023-09-05 10:41:54 +02:00
Sunik Kupfer
1c419cd75c Add setting to ignore VPNs at connection detection (bitfireAT/davx5#356)
* Add setting to ignore VPNs at connection detection

* Minor changes

- move methods to ConnectionUtils to keep SyncWorker class compact
- always use "ignore VPNs" as Boolean
- other minor changes

* Show ignore VPNs setting only below api lvl 23

* Change strings

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-09-05 10:38:38 +02:00
Arnau Mora
62cca2939a Upgrade AppIntro to 7.0.0-beta02 (bitfireAT/davx5#357)
Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2023-09-05 10:37:16 +02:00
Arnau Mora
795ae49da4 SyncWorker "soft error (max retries reached)" notification confusing for users (bitfireAT/davx5#353)
* Sync error notification dismiss

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* FIXME

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added click intent

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Delayed error info

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added tag for max retries

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Reduced priority

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Removed max retries tag

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Using account name as tag

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added authority to notification tag

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added account type to notification tag

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Changed priority to min

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2023-09-05 10:37:08 +02:00
Arnau Mora
bd7b2714d2 Upgrade AGP to 8.1.1 (bitfireAT/davx5#355)
* Upgrade AGP to 8.1.1

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Upgrade Kotlin and dependencies

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Downgrade browser

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2023-09-05 10:37:00 +02:00
Ricki Hirner
273deecbe4 Update dependencies 2023-08-16 22:59:20 +02:00
Arnau Mora
a375a16edf Added tooltip to sync collections FAB (bitfireAT/davx5#340)
* Added tooltip to sync collections fab

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Greyscale tint for collections sync FAB

* Always use TooltipCompat for FAB tooltips

Closes bitfireAT/davx5#339

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Sunik Kupfer <kupfer@bitfire.at>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-08-15 18:23:27 +02:00
Sunik Kupfer
4ce33e949b Use AccountManager.setAndVerifyUserData extension method, instead of setUserData (bitfireAT/davx5#344)
Hopefully fixes bitfireAT/davx5#308

* Add new setAndVerifyUserData extension function to AccountManager
* Use new setAndVerifyUserData extension function instead of insecure setUserData
* Update KDoc [skip CI]

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-08-15 18:23:00 +02:00
Ricki Hirner
fcb9d7f560 Fetch translations from Transifex 2023-08-07 20:45:55 +02:00
Ricki Hirner
3709c2ab32 Version bump to 4.3.5.2 2023-08-07 20:44:00 +02:00
Ricki Hirner
8aeb2d5064 Fix onPerformSync crash on InterruptedException (bitfireAT/davx5#343)
- use Kotlin and CompletableDeferred instead of Java synchronization
- signal cancellation by completing CompletableDeferred instead of Thread.currentThread.interrupt()
2023-08-07 20:43:51 +02:00
Arnau Mora
f806122b00 Upgrade AGP to 8.1 and configure per app language (bitfireAT/davx5#338)
* Upgraded AGP

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Enabled automatic locale config generation

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added fallback language

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added legacy service

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added `Accept-Language` header to custom tabs

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Nextcloud Login Flow/Google OAuth: also send language tag for default locale

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-07-28 10:53:57 +02:00
Ricki Hirner
4f192c253d Use KSP for Room (bitfireAT/davx5#333) 2023-07-27 18:20:44 +02:00
Ricki Hirner
7038bbf70a Google OAuth: fix manifest merging so that intent-filter for OAuth callback is included again (#367) 2023-07-27 18:13:57 +02:00
Ricki Hirner
b7e00fac40 Fetch translations from Transifex 2023-07-22 12:23:44 +02:00
Ricki Hirner
13d9ce089b Bump version to 4.3.5.1 2023-07-22 12:22:28 +02:00
Ricki Hirner
d74e8da5c1 Update dependencies 2023-07-22 12:22:26 +02:00
Arnau Mora
a2b633edc0 Updated Google login button (bitfireAT/davx5#332)
* Updated Google login button

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Moved Google G logo file

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Changed background color

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Amend warning sign [skip CI]

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-07-22 12:22:22 +02:00
Ricki Hirner
e6125dd644 Update README, add Mastodon button [skip CI] 2023-07-16 12:14:48 +02:00
Ricki Hirner
162c9effe0 Update to latest dav4jvm (uses Java 8 Time API) (bitfireAT/davx5#329)
Fixes WebDAV file timestamps always being null (see bitfireAT/dav4jvm#22)
2023-07-14 23:11:44 +02:00
Arnau Mora
c62874a34b Make "Refresh collection list" more visible (bitfireAT/davx5#266)
* Added refresh collections fab

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added listener for clicks on refresh collections fab

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Adjusted sizing

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Updated tooltip and description for collections sync

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed Snackbar

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added warning for null serviceId

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Changed refresh collections service id fetching method

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Tooltip updates on refresh collections list

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Migrate to ViewPager2; show "Refresh collections" for WebCal, too

* Added refresh collections fab

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added listener for clicks on refresh collections fab

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Adjusted sizing

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Updated tooltip and description for collections sync

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed Snackbar

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Added warning for null serviceId

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Changed refresh collections service id fetching method

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Tooltip updates on refresh collections list

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Migrate to ViewPager2; show "Refresh collections" for WebCal, too

* Changed collections refresh action update method

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Use lambda syntax for observers

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
Co-authored-by: Sunik Kupfer <kupfer@bitfire.at>
2023-07-14 23:11:32 +02:00
Ricki Hirner
36e377001f Clean up unused resources
- also fixes some wrong resources
2023-07-11 11:47:16 +02:00
Ricki Hirner
400d46fce2 Amend comments for repository identity 2023-07-10 15:22:24 +02:00
Ricki Hirner
ec34da1ace Google Login: add Limited Use policy (bitfireAT/davx5#327) 2023-07-10 15:14:21 +02:00
Ricki Hirner
bb6ed7ec64 Fetch translations from Transifex 2023-07-10 15:13:23 +02:00
Ricki Hirner
042b2371fb Version bump to 4.3.5 2023-07-10 15:12:04 +02:00
Ricki Hirner
5272943a05 Google: use new API base URL (bitfireAT/davx5#324)
Google: use new API base URL (finds secondary calendars and homesets)
2023-07-07 11:30:22 +02:00
Sunik Kupfer
318939e970 Ensure internet connection before syncing (bitfireAT/davx5#305)
* Comment out failing test

* Check internet connection before syncing for API 23+

* [Skip CI] Amend comments

* Comment out whole test class

* Comment out whole test class

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-07-06 23:45:08 +02:00
Ricki Hirner
1478306e54 Comment out failing test 2023-07-06 23:45:06 +02:00
Sunik Kupfer
c7ff42c03f [Google OAuth] fix Re-authentication crash (bitfireAT/davx5#319)
* Hide/Show auth settings instead of removing them prevents NPE

* Provide original OAuth login email as default value for GoogleLoginFragment started from account settings

* Account settings: explicitly trim empty user name to null

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-07-06 23:45:01 +02:00
Ricki Hirner
17ccb305f9 Configuration detection: allow null Credentials (anonymous login) (bitfireAT/davx5#323) 2023-07-06 23:44:11 +02:00
Sunik Kupfer
37a7ebb9c0 Fix test again (bitfireAT/davx5#321) 2023-07-06 23:44:08 +02:00
Sunik Kupfer
1c403d171c Fix test (bitfireAT/davx5#320) 2023-07-06 23:44:00 +02:00
Arnau Mora
81273f028e Added known base urls (bitfireAT/davx5#315)
* Added known base urls

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Change order

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-07-03 12:07:57 +02:00
Sunik Kupfer
fac68680ee Block sync adapter's onPerformSync until SyncWorker finishes (bitfireAT/davx5#278)
* Block sync framework until SyncWorker finishes

* Bump version code for 4.3.3 (previous version code was never released publicly)

* Fetch translations from Transifex

* Release internal version automatically [skip ci]

* Update periodic sync workers when "Sync only on WiFi" flag is changed (#282)

* Update periodic sync workers when "sync only on WiFi" flag is changed
* Remove BootCompletedReceiver which was only needed to repair sync intervals (not required with WorkManager anymore)

* Bump version code to 403030006 (stays 4.3.3)

* Use unique worker name, Java notify/wait and observeForever

* Remove observer when sync finished

* Catch and ignore, but log interruption exceptions

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-07-03 12:07:39 +02:00
Ricki Hirner
46488f1618 Version bump to 4.3.5-alpha.1; update Compose; move GoogleLoginFragment to fix build 2023-06-30 13:51:09 +02:00
Sunik Kupfer
0f92b0fb05 291 clean up oauth mess (bitfireAT/davx5#307)
* Move GoogleOAuth members into GoogleLoginFragment

* Require login flow capable browser and notify user if missing

* Receive AppAuth redirects only in standard and gplay flavor

* Set davx5 as user-agent for AppAuth connection builder

* Re-authentication in Account settings

* Catch unauthorized exceptions at collection refresh and notify user to re-authenticate

* Suggest email address on account creation

* Set contact groups default setting as per-contact categories for oauth logins

* Add authentication to debug info, minor other changes

* Better error handling; don't pre-set group type

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-06-30 12:53:16 +02:00
Ricki Hirner
6a2c366358 Update dependencies; CI: don't checkout submodules 2023-06-28 22:42:24 +02:00
Arnau Mora
4ba50b969a Migrated to ical4android library (bitfireAT/davx5#302)
* Migrated to ical4android library

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Manually excluded Groovy

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Updated ical4android

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Update dependencies

* Added more exclusions

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Also update Room [skip CI]

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-06-28 22:30:53 +02:00
Michael Biebl
473dca62fa Shorten version hash of vcard4android library (#345)
Looks better in AboutLibraries and is more consistent with cert4android
and dav4jvm.
2023-06-28 12:53:43 +02:00
Arnau Mora
11c8866614 Security: check/fix DebugInfoActivity: Uncontrolled data used in path expression (bitfireAT/davx5#267)
* Passing file name to `EXTRA_LOG_FILE` instead of path. Replaces illegal chars

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed log file extra

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Removed log file extra

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added more error logging

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Update KDoc, remove strings

* Moved ViewModel initialization to Factory

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Changed ViewModel factory

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Updated annotations

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Handle EXTRA_LOGS correctly

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-06-28 12:52:57 +02:00
Arnau Mora
3ba463dbd4 Removed Twitter from navigation drawer (bitfireAT/davx5#312)
Signed-off-by: Arnau Mora <arnyminerz@proton.me>
2023-06-26 08:53:19 +02:00
Arnau Mora
837fcc7805 Removed Twitter links (#351) 2023-06-25 14:11:24 +02:00
Arnau Mora
f820e92d15 Integrate vcard4android as an android library (bitfireAT/davx5#300)
* Migrated to external dependency for vcard4android

Signed-off-by: Arnau Mora <arnyminerz@proton.me>

* Removed vcard4android submodule

* Update vcard4android version, remove from settings.gradle

---------

Signed-off-by: Arnau Mora <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-06-14 14:21:10 +02:00
Ricki Hirner
1b521e3c17 Bump version code 2023-06-13 21:01:10 +02:00
Ricki Hirner
50cb223bb6 DavResourceFinder: make input arguments explicit (bitfireAT/davx5#298) 2023-06-13 21:00:56 +02:00
Ricki Hirner
764c382af3 Fetch translations from Transifex 2023-06-13 18:47:08 +02:00
Ricki Hirner
aed721409f Bump version to 4.3.4.1 2023-06-13 18:43:06 +02:00
Ricki Hirner
24a3954d75 Google OAuth: fix endless loop in Fragment (bitfireAT/davx5#295)
Fix endless loop
2023-06-13 18:40:49 +02:00
Sunik Kupfer
ba36d01e11 [Google OAuth] Support custom Client IDs (bitfireAT/davx5#294)
* Support custom Client IDs

* Refactoring

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-06-13 18:39:40 +02:00
Ricki Hirner
ff4af11bcf Minor HttpClient change 2023-06-11 14:30:48 +02:00
Ricki Hirner
66c93414c0 Fetch translations from Transifex 2023-06-11 14:30:24 +02:00
Ricki Hirner
278171dbac Bump version to 4.3.4 2023-06-11 14:29:28 +02:00
Ricki Hirner
5b38943205 Support OAuth for Google (bitfireAT/davx5#289)
* Proof of concept (without auth state storage)
* Implement Bearer authentication, create network package
* Properly create/dispose AuthService
* Use proper ActivityResultContract
* Integrate into default login activity
* Change client ID to davx5integration@gmail.com
* Google Login: adapt login view
* Fix tests
* Don't allow empty Google account
* Move strings to resources
2023-06-11 14:17:50 +02:00
Sunik Kupfer
1b3fde0854 280 fix problematic toasts (bitfireAT/davx5#283)
* When renaming show a toast when account with new name exists already

* Replace toasts with snackbars

* Apply code styling hints

* Move lambdas out of parentheses

* Inject application instead of context

* Replace Toast with Snackbar

* Shorten messages shown in snackbars

* Show Toast in UI instead of model; use AndroidViewModel

* Move account name check out of lock; don't close activity if not successful

* Duplicate account name check: only check for our account type

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-06-09 17:26:11 +02:00
Sunik Kupfer
caf7be5e11 New setting to preselect only personal collections (bitfireAT/davx5#276)
* Add new preselect_collections option and deprecate sync_all_collections option

* Optimize imports

* At refresh, decide on whether a collection should be preselected

* Add preselect_collections_blacklist setting and restriction

* Adhere to preselect_collections_blacklist setting

* Add preselect_collections values

* Also check for empty regex string and use new setting values

* Add unit tests

* Remove sync_all_collections setting and restriction

* Blacklist nextclouds recently contacted addressbook in restriction and setting by default

* Improve kdoc

* KDoc, changed setting names, minor code optimizations

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-06-06 21:03:50 +02:00
Ricki Hirner
3b2246e74b Bump version code to 403030006 (stays 4.3.3) 2023-06-01 19:12:47 +02:00
Ricki Hirner
12902a051f Update periodic sync workers when "Sync only on WiFi" flag is changed (bitfireAT/davx5#282)
* Update periodic sync workers when "sync only on WiFi" flag is changed
* Remove BootCompletedReceiver which was only needed to repair sync intervals (not required with WorkManager anymore)
2023-06-01 19:12:41 +02:00
Ricki Hirner
bed762ab7d Fetch translations from Transifex 2023-06-01 12:31:23 +02:00
Ricki Hirner
b56dd96fd8 Bump version code for 4.3.3 (previous version code was never released publicly) 2023-06-01 12:30:11 +02:00
jgirardet
cb5f722a87 Add mailo.com to known base urls (#329)
* Add mailo.com to known base urls

* fix order
2023-05-31 09:51:04 +02:00
Ricki Hirner
dceb1493eb Remove cert4android from settings.gradle, remove obsolete adb-log.sh, update ical4android 2023-05-30 17:09:37 +02:00
Arnau Mora
17718a7ed8 Integrate cert4android as an android library (bitfireAT/davx5#274)
* Removed `cert4android` submodule

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added cert4android dependency

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Update dependencies, fix invalid style

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-05-30 11:10:06 +02:00
Arnau Mora
6d278b7e04 Made collection url selectable (bitfireAT/davx5#275)
Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
2023-05-23 17:02:33 +02:00
Sunik Kupfer
ae303e3e8d Refactor queryHomeSets method for clarity (bitfireAT/davx5#270)
* Make it clear that the first call of recursive method queryHomeSets is for the current-user-principal home set

* Update whitespace in kdoc

* Update kdoc
2023-05-23 17:02:26 +02:00
Ricki Hirner
38cfd200b1 Fetch translations from Transifex 2023-05-22 08:27:33 +02:00
Ricki Hirner
968db83366 Version bump to 4.3.3 2023-05-22 08:26:46 +02:00
Ricki Hirner
8923f9b1ad Fix workflow concurrency 2023-05-17 22:15:49 +02:00
Ricki Hirner
e60723042b Cancel running actions for previous commits if there are new pushes on the same branch/tag 2023-05-17 18:24:15 +02:00
Ricki Hirner
5fee2375a5 Update dependencies, bump version to 4.3.3-rc.1 2023-05-17 18:13:27 +02:00
Ricki Hirner
7f62148c75 Disable configuration cache for release builds (closes bitfireAT/davx5#263) (bitfireAT/davx5#269) 2023-05-17 18:13:27 +02:00
Sunik Kupfer
a072cf0404 Add last synced time to collection properties (bitfireAT/davx5#260)
* Add last synced time to collection properties

* Show last synced time for every to the collection relevant authority

* Try finding the application name for given package name

* Resolve authority to package name before finding application label

* Use AndroidViewModel instead of ViewModel; change model LiveData to val

* Rewrite to Compose, use relative time description

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-05-17 18:13:27 +02:00
Ricki Hirner
5aff37a9a6 Add style guide 2023-05-17 18:13:27 +02:00
Ricki Hirner
2f1757bb78 Version bump to 4.3.3-beta.2
- minor workflow changes
2023-05-17 18:13:27 +02:00
Ricki Hirner
34cc3ac670 Update ical4android (increase minimum jtx Board version) and vcard4android (fixes NoYearOfEra bug) 2023-05-17 18:13:27 +02:00
Ricki Hirner
55ee89529f Update dependencies, including cert4android and vcard4android 2023-05-17 18:07:17 +02:00
Ricki Hirner
83b5b939e1 Add issue template file so that we can enable issues for ourselves 2023-05-12 11:25:37 +02:00
Ricki Hirner
b543dc9332 Fetch translations from Transifex 2023-05-12 10:50:46 +02:00
Ricki Hirner
28466ace46 Version bump to 4.3.3-beta.1 2023-05-12 10:49:38 +02:00
Sunik Kupfer
63f51c4b42 Account settings migration 13 to 14 (bitfireAT/davx5#262)
* AccountSettings: prevent redundant calls, move migrations to separate class

* Don't create AccountSettings while migrating

* Double thread sleep time to wait for sync framework to get disabled, and update logging

* Remove manual testing helper

* Fix tests

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-05-12 10:23:47 +02:00
Ricki Hirner
8ac8971293 Update dav4jvm (WebDAV timestamps), ical4android (jtx geofence x radius), Github workflow actions 2023-05-12 00:14:05 +02:00
Arnau Mora
9748b75671 Fixed toast threading (bitfireAT/davx5#256)
* Fixed toast threading

* Added `AndroidViewModel.context`

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Moved to `AndroidViewModel`

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Changed error invocation

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Added back constructor inject

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>

* Save Application with val instead of util method

* Use LiveData for error message

---------

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-05-11 18:41:07 +02:00
Sunik Kupfer
2ebb40b152 Implement proper network check before manual sync (bitfireAT/davx5#259)
* Implement proper network check before manual sync

Do a proper connectivity check, to show a message to the user, about the sync being scheduled for when connectivity is available, before enqueueing the SyncWorker.

* Update the sync status in accounts adapter to include pending syncs

This is relevant for when a manual sync is triggered without connectivity.

* Replace Toast by Snackbar

* Replace ViewModel by AndroidViewModel

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-05-11 18:40:29 +02:00
Ricki Hirner
5cbf14fa27 Update ical4android to fix TZ with negative DST (Google Europe/Dublin) bug 2023-05-10 13:21:12 +02:00
Ricki Hirner
2ff4b35bdb Use configuration cache to improve build speed (closes bitfireAT/davx5#248) (bitfireAT/davx5#255)
- disable per-app language preference for now (will be enabled again with bitfireAT/davx5#245)
- disable Jetifier (doesn't seem to be explicitly required for in-app rating API anymore)
- remove Locator dependency
- update ical4android
2023-05-10 13:20:57 +02:00
Ricki Hirner
7b2ffeb98e Update dependencies 2023-05-09 12:36:45 +02:00
Ricki Hirner
9917f7131a Comment out AccountSettingsTest for now because they cause strange failures 2023-05-07 08:03:21 +02:00
Ricki Hirner
1c474df606 Version bump to 4.3.3-alpha.1 2023-05-07 08:03:21 +02:00
Ricki Hirner
c3cd970581 Use Jetpack Compose for AboutLibraries (bitfireAT/davx5#246)
- add JetPack Compose dependencies
- use existing XML theme as Compose MdcTheme
- update AboutLibraries to newest version (uses Compose)
- embed AboutLibraries Compose into existing Fragment layout
2023-05-07 08:03:21 +02:00
Ricki Hirner
0bed00c05a Handle InvalidAccountException in SyncAdapterService 2023-05-07 08:03:21 +02:00
Sunik Kupfer
f9d6bb153c Workmanager scheduled sync (bitfireAT/davx5#216)
Closes bitfireAT/davx5#14

* extract WorkManager util functions and kdoc
* add PeriodicSyncWorker
* refactor and some kdoc
* use PeriodicSyncWorker and add todos
* allow SyncAdapter arguments to be passed to SyncWorker and have re-synchronization use SyncWorker
* handle sync cancellation in AccountSetting migration
* handle sync cancellation when account is renamed
* remove sync frameworks global sync setting and sync state awareness for the GUI
* Observe sync worker status in AccountListFragment
* Create setup for stepwise refactor of the sync adapters. This should keep the app from failing to build.
* Create new abstract BaseSyncer class for generic sync code. Adapt consumers and tests to use the new class.
* Move calender sync code to new CalenderSyncer class which is independent from the sync framework
* In CalendarSyncAdapterService pass sync requests from Sync Adapter Framework to SyncWorker
* Use CalenderSyncer in SyncWorker
* Move contacts sync code to new ContactsSyncer class.
* Move address book sync code to new AddressBookSyncer class
* Move jtx sync code to new JtxSyncer class
* Move tasks sync code to new TaskSyncer class
* Remove duplicate code in inherited sync adapters.
* Remove refactoring helper interface, duplicate generic sync code and some linting.
* Adapt tests for new Syncer class and move to the new package
* Remove remaining duplicate code in SyncAdapterService, add todos and edit kdoc.
* Move all the single line sync adapter services into one file.
* Remove concurrent sync runner code and its test, as we now use WorkManagers one time work requests.
* Remove SAF manual sync flag usage where unnecessary.
* Drop ability to prioritise collections for sync, as not used and hindering removal of sync adapter arguments.
* Pass simple string array to SyncWorker instead of bundle to simplify code.
* Restructure work query code.
* Get debug info from account settings and WorkManager.
* Write tests for PeriodicSyncWorker
* Test account creation will set a default sync interval for CardDAV and CalDAV
* Throw an exception if accountManager returns null for sync interval value
* Do proper interval check and add tests for AccountSettings
* Use work manager query to determine whether work is in a specific state
* [WIP] Add test to check that task provider is configured correctly on account creation
* Edit test checking that task sync is configured correctly on account creation with/without installed task app(s), by mocking TaskUtil
* Edit test such that it does not require a flaky flag
* Remove periodic sync when tasks app is uninstalled
* Bring back content triggered syncs
* Rename enqueueSyncWorker method to enqueue only for clarity
* Enable SyncAdapterFramework to cancel running SyncWorker
* Add test ensuring that SyncWorker.onStopped() interrupts the running sync thread
* Add retry policy sync on soft errors
* Check users sync conditions before enqueueing SyncWorker
* Add test for whether user sync conditions are treated correctly and kdoc
* Rename ambiguous shorthand "SAF" to "SyncFramework", as SAF usually means StorageAccessFramework
* Migration: Disable sync framework periodic syncs when interval is changed for specified authority
* Add Workers info to debug info
* Use WorkInfo.runAttemptCount to fail work after 20 soft errors
* Notify user if retry limit for soft errors has been reached
* Remove left over concurrency sync run tests prevention
* Migration: Continue to remove periodic sync framework syncs until user migration to PeriodicSyncWorker syncs is complete
* Kdoc and small changes
* Migration: Change to hard migration strategy
* Drop repairSyncIntervals method in favor of hard migration strategy
* Improve debug info of workers
* Remove sync framework periodic syncs, created by enabling content triggered syncs
* Change minimum sync interval to 15 min; minor other changes
* Fix tests
* Implement requested changes and update kdoc
* Add network connectivity restrictions to PeriodicSyncWorker
* Minor changes
* Move back sync classes to syncadapter package for now (can be separated later)
* Add KDoc
* Changes from review
* Rename test methods
* Add back global sync status warning

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-05-07 08:03:21 +02:00
Ricki Hirner
299fb27af4 Fetch translations from Transifex 2023-05-02 14:06:43 +02:00
Ricki Hirner
9c51dadce8 Version bump to 4.3.2 2023-05-02 13:57:31 +02:00
Ricki Hirner
ae3f5c8bab Fix ProGuard rules for R8 (AGP 8.0) 2023-05-01 11:00:37 +02:00
Ricki Hirner
aa063adaba Upgrade gradle, AGP, dependencies 2023-04-29 22:50:18 +02:00
Sunik Kupfer
c5b02d2bde Log warning if principal URL does not provide CalDAV/CardDAV service (bitfireAT/davx5#242)
* Log warning if principal URL does not provide CalDAV/CardDAV service

* Unified log messages for identical cases

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-04-20 14:01:30 +02:00
Arnau Mora
61b6cd5e2e Disabled GPS feature requirement (bitfireAT/davx5#241)
* Disabled GPS feature requirement

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Updated GPS feature comment

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

---------

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>
2023-04-18 11:11:04 +02:00
Ricki Hirner
aecabb9845 Update dependencies 2023-04-13 11:25:51 +02:00
Michael Biebl
16573e9ea6 Sync interval failure (#235)
* Fix log message

* Log a warning if repairing the sync interval failed
2023-04-12 11:41:24 +02:00
Ricki Hirner
e2bfda71bc Use large runners for CI Android tests (#306) 2023-04-12 11:39:32 +02:00
Ricki Hirner
133d155b94 Update ical4android; CI: use gradle/gradle-build-action for caching 2023-03-31 16:12:20 +02:00
Sunik Kupfer
874ad7b1c6 Handle error responses when querying principals for display names (closes bitfireAT/#237) (bitfireAT/#238)
* Add test with inaccessible principal

* Catch and log exceptions where server resource is inaccessible

* Catch and log every HttpException
2023-03-31 13:29:20 +02:00
Sunik Kupfer
fc17d0ed69 SettingsActivity resync now uses SyncWorker (bitfireAT/davx5#226)
* Alter the SyncWorker sync request to allow passing sync arguments along

* Request sync in SettingsActivity via SyncWorker instead of ContentResolver

* Don't use catch-all arguments as worker arguments

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-03-28 16:59:36 +02:00
Ricki Hirner
2f8340a646 SyncManager: fix logging string that always talked about address data even for events/tasks 2023-03-24 11:52:19 +01:00
Ricki Hirner
6050396a45 Update Transformations to new syntax (androidx.lifecycle:lifecycle-viewmodel-ktx:2.6) 2023-03-24 10:46:42 +01:00
Ricki Hirner
2503d92dca Update dependencies, including ical4android (now ignores invalid properties)
Closes bitfireAT/davx5#165
2023-03-23 17:09:37 +01:00
Ricki Hirner
983a142670 Add CodeQL security scanning (#296)
* Create .github/workflows/codeql.yml

* Build explicitly instead of using autobuild (because it doesn't work)

* Checkout submodules and cache Java dependencies
2023-03-23 11:29:29 +01:00
Arnau Mora
a3a8317b2f Fix fetching list of translators (bitfireAT/davx5#232)
* Changed `password` with `token`

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Fetch script: don't use --minimum-perc 20 because it's in .tx/config

---------

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-03-21 15:46:58 +01:00
Ricki Hirner
880abf024c Fetch translations from Transifex 2023-03-20 13:10:34 +01:00
Ricki Hirner
83936b97ea Bump version to 4.3.1.1 2023-03-20 12:58:10 +01:00
Ricki Hirner
50dbd66d3a Version bump to 4.3.1 2023-03-13 18:01:20 +01:00
Arnau Mora
17fb1168cc Generate locales with Locator (bitfireAT/davx5#218)
* Added locator plugin

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Removed old generator

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Updated source sets

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Added locator submodule

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Removed locator submodule

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Moving scripts

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Update dependencies

* Added `LocatorPlugin`

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Fixed locales generation

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Removed buildSrc

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Using plugin from Jitpack

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Using plugin from Gradle

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Removed comment

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Remove jitpack

---------

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-03-13 18:01:13 +01:00
Ricki Hirner
89a698e7b2 Bump version to 4.3.1-beta.1 2023-03-02 13:11:09 +01:00
Ricki Hirner
6dd0af2d7a Update Kotlin and gradle plugin 2023-03-02 13:11:08 +01:00
Patrick Lang
29a97c85ff Fixed language selector icon for night mode (bitfireAT/davx5#223)
Update ic_language.xml

Replaced tint by  android:tint="?attr/colorControlNormal">
2023-03-02 13:11:01 +01:00
Sunik Kupfer
9fee782968 Check sync workers properly (closes bitfireAT/davx5#217) (bitfireAT/davx5#220)
* check sync workers properly

* Move utils around

* Merge live data, instead of observing in view, to recalculate account list.

* Update LiveData with same value

* Remove unused context property.

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-02-28 10:28:52 +01:00
Sunik Kupfer
fe7a928064 Provide owner url in jtx collection (bitfireAT/davx5#221)
* provide owner url in jtx collection

* Updated contract, added owner displayname to collections sync

* Update ical4android

---------

Co-authored-by: Patrick Lang <72232737+patrickunterwegs@users.noreply.github.com>
2023-02-28 10:28:46 +01:00
Ricki Hirner
54c6117ba2 Update dependencies 2023-02-28 10:28:29 +01:00
Arnau Mora
fffb029362 Fixed missing languages and wrong display (bitfireAT/davx5#214)
* Displaying language with `displayName` instead of `displayLanguage`

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Added languages from `davdroid`

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Fixed wrong locale tags

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Using Map instead of list for language loading

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Typo

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Changed language loading logic

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Improved docs

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Moved method from `LangUtils`

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Using regex

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Added test for `resourceQualifierToLanguageTag`

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

---------

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>
2023-02-28 10:28:19 +01:00
Sunik Kupfer
b83d116f40 show real name of davowner (bitfireAT/davx5#208)
* add principal table with dao

* add principal table with dao

* collection saves ownerId instead of owner URL

* save and refresh principals

* show display name of collection owner in GUI

* show only the owner name (preferably) or respective url

* remove principals which do not own any collections

* Don't mock AppDatabase

* ensure we are really dealing with a principal and save it even without its display name

* ensure owner label is hidden when neither owner-displayname nor  owner-url are available

* save principal urls without trailing slash

* use a custom query to find principals without collections

* Some changes

- insertOrUpdateByUrl
- don't explicitly set id=0 when not necessary,
- make it work when there are already entries with trailing slahes
- added TODOs

* Small changes
- Update principal only if display name changed
- Rename methods
- Kdoc
- Tests

* stop using simple methods with vague names

* rename method insertOrUpdateOrGet to insertOrUpdate and leave existing kdoc for explanation

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-02-20 13:29:56 +01:00
Ricki Hirner
26961d9749 Update dependencies 2023-02-20 13:29:52 +01:00
Patrick Lang
7b34f8148f jtx-board-2-3-new-recur-handling (bitfireAT/davx5#196)
* adapted JtxSyncManager, added new method in LocalJtxCollection

* minor code improvement

* inserting recurring entry as well, as the series might not have been processed yet

* version bump (preliminarily)

* version bump (preliminarily)
2023-02-20 13:29:44 +01:00
Ricki Hirner
4dd79606f2 F-Droid changelog for 4.3 2023-02-08 14:04:40 +01:00
Ricki Hirner
f13164af41 Fetch translations from Transifex, tx config changes 2023-02-08 12:39:40 +01:00
Ricki Hirner
43b6b8b3db Version bump to 4.3 2023-02-08 12:33:16 +01:00
Ricki Hirner
d00966063e Update ical4android 2023-02-08 12:33:15 +01:00
Arnau Mora
6990479d18 Enabled automatic popup of the custom certificate dialog (bitfireAT/davx5#207)
* Enabled automatic popup of the custom certificate dialog

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Use chaining instead of .let

---------

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-02-08 12:32:29 +01:00
Ricki Hirner
1dd93ae43a Collections fragment: cache paging data (bitfireAT/davx5#206)
Closes bitfireAT/davx5#205
2023-02-07 17:14:21 +01:00
Michael Biebl
179602df46 Drop duplicated .ForegroundService (#266)
Was added by mistake in cfee0f3461.
2023-02-07 17:13:04 +01:00
Ricki Hirner
412acbcc7d Version bump to 4.3-rc.2 2023-02-03 21:08:24 +01:00
Ricki Hirner
dc207682a5 CollectionsFragment: Replace Kotlin flow by LiveData (closes bitfireAT/davx5#201) (bitfireAT/davx5#204) 2023-02-03 21:08:14 +01:00
Ricki Hirner
c8c1124303 Fix release workflow 2023-02-03 21:07:35 +01:00
Ricki Hirner
00235e12a4 Fetch translations from Transifex 2023-02-01 14:02:59 +01:00
Ricki Hirner
e283f5f34b Version bump to 4.3-rc.1 2023-02-01 13:27:56 +01:00
Ricki Hirner
2349468b0b Update dependencies, including ical4android 2023-02-01 13:27:55 +01:00
Sunik Kupfer
cf4b7891e3 fix collections vanishing and remove useless home set reference (bitfireAT/davx5#199) 2023-02-01 13:10:35 +01:00
Sunik Kupfer
ca62efa2aa remove DaoTools (for syncing) (bitfireAT/davx5#190)
* add test fixtures

* Use direct DB access instead of DaoTools

* minor changes and kdoc

* minor changes and kdoc

* remove obsolete DaoTools, SyncableDao and IdEntity classes

* KDoc

* use hashmap instead of list and added kdoc

* always load from database

* add a test and make test structur more flexible for future tests

* minor KDoc

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-01-31 13:40:39 +01:00
Ricki Hirner
2661dbba34 Use Worker for account cleanup (bitfireAT/davx5#197)
* [WIP] Use Worker for account cleanup

* Remove AccountsUpdatedListener Hilt module

* Use 15 seconds, provide Hilt module for App creation
2023-01-28 21:09:58 +01:00
Ricki Hirner
954f4dcaa3 Debug info: count jtx Board tasks correctly (bitfireAT/davx5#195)
* Reformat code

* Use sync-adapter URI to count jtx Board entries

* Debug info: restrict entry counts per withSyncAdapter URI and not over WHERE clause
2023-01-28 21:09:49 +01:00
Ricki Hirner
6c8976f6ff Version bump to 4.3-beta.1 2023-01-20 11:12:33 +01:00
Ricki Hirner
29b2b2372f Catch SecurityException of notifications 2023-01-20 11:12:32 +01:00
Ricki Hirner
5156ff1380 Update dependencies 2023-01-20 11:12:32 +01:00
Sunik Kupfer
62617a2889 168 refactor service detection part 2 (bitfireAT/davx5#174)
* [WIP] refactor

* [WIP] refactor

* save, update and delete homesets one by one

* save, update and delete collections one by one

* cleaner code

* prevent jumps in row ids

* [WIP] small changes and kdoc

* remove duplicate code and add kdoc

* improve kdoc

* remove redundancy based on service type in resource finder

* tests setup

* handle cancellation

* add tests

* Don't use IdEntity for service detection anymore

* HomeSetDao: getByUrl requires a service (there may be two accounts with the same homeset URLs, for instance with different credentials)

* Deprecate DaoTools

* Minor changes

* Add TODO

* use self explanatory variables instead of a pair

* update kdoc

* add unfinished tests

* add test for updating a collection

* add test for preserving collection flags

* mark collections as homeless if not rediscovered in its homeset

* proper implementation of update and delete of homeless collections with test

* minor changes and kdoc

* Tests: adapt mock server 404

* KDoc

* get collections by service and url, deprecate getByUrl()

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-01-19 23:32:15 +01:00
Patrick Lang
df2922f873 Added intent-filter for AccountActivity (bitfireAT/davx5#187)
* Added intent-filter for AccountActivity

* removed intent filter for account activity
2023-01-19 23:31:57 +01:00
Flo Edelmann
861bcf30b4 Add dav.mailbox.org to known base URLs (#251)
* Add dav.mailbox.org to known base URLs

* Replace mailbox.org by dav.mailbox.org

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-01-11 23:51:35 +01:00
Arnau Mora
bee7cf20ac 169 webdav handle weak etags correctly (bitfireAT/davx5#185)
* Added `GetETag.NAME` fetching when `PROPFIND`ing

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* `GetETag.eTag` is only taken if tag is not weak

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Using `GetETag` instead of `QuotedStringUtils` and ignoring weak eTags

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Updated `dav4jvm`

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Fixed checks

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Use new dav4jvm where weak can't be null

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2023-01-09 17:57:04 +01:00
Sunik Kupfer
5434291c6c fixes data saver warning showing below android 7 (bitfireAT/davx5#180)
post false to live boolean dataSaverEnabled even if below api lvl 24
2023-01-09 17:56:56 +01:00
Bernhard Stockmann
8dc9f2e415 Update README.md 2023-01-04 14:55:11 +01:00
Arnau Mora
53bd4866c3 Added Mastodon link to navigation drawer (bitfireAT/davx5#179)
* Added mastodon icon

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Added mastodon link to drawer

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Added mastodon link action

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Mastodon icon tint

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Added Mastodon icon to main

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>
2023-01-01 23:26:54 +01:00
Michael Biebl
1f4398b21e Use consistent margins between different permission switches 2022-12-22 21:25:12 +01:00
Ricki Hirner
12bb09ecb8 Minor CI changes 2022-12-22 21:22:56 +01:00
Ricki Hirner
4c71d21d7b WorkManager address book sync: don't run calendar/tasks sync on address book accounts; vcard4android: set N:LastName for vCard3 groups 2022-12-22 21:22:56 +01:00
Patrick Lang
1f818ee3b4 Added language selection (bitfireAT/davx5#137)
* added language selection

* PermissionsFragment: Hide notification permission switch on Android < 13

* Retrieve locales with a function

* Added locales flavoring

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Moved locales functions to `locales.gradle`

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Added `locales_config.xml` generation

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Now gets generated automatically

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Added `android:localeConfig`

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Updated submodules

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

* Using `logger` instead of `println`

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>

Signed-off-by: Arnau Mora <arnyminer.z@gmail.com>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
Co-authored-by: Arnau Mora <arnyminer.z@gmail.com>
2022-12-22 21:22:55 +01:00
Michael Biebl
0521f4bcf0 Trivial indentation fixes (#226) 2022-12-22 21:22:55 +01:00
Ricki Hirner
2730d14cb9 Version bump to 4.3-alpha.1 2022-12-22 21:22:55 +01:00
Sunik Kupfer
cfee0f3461 work manager manual sync (bitfireAT/davx5#109)
* rabase with dev branch

* added test for checking whether manual work-manager sync queues worker

* overwrite getForegroundInfo to show a "sync running" notification, to run expedited work on Android <12

* basic error state with sensible feedback from syncframework

* remove integer state flags and pass SyncResult as string

* Manual sync cancellation

* rabase with dev branch

* Minor changes

- add Jtx Board sync adapter to sync worker
- use new notification ID for sync worker

* status bar reflects sync of SyncWorker and sync framework correctly

* [WIP] custom hilt SyncComponent

* fix autoclose cast not available below api24

* SyncScope implementation using WeakReference

* Remove unnecessary logging call

* AddressBooksSyncAdapter.sync uses SyncWorker instead of ContentResolve to call requestSync

* add some code documentation

* move all utility objects into one package

* Also check SyncWorker state for accounts list sync status bar

* clean up imports

* Remove duplicate copyright notices

* Minor changes

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2022-12-15 16:04:39 +01:00
Michael Biebl
262592a3d9 Remove duplicate copyright header (#218) 2022-12-11 17:03:27 +01:00
Ricki Hirner
18a3c0e2fd Fetch Translations from Transifex 2022-12-10 13:20:31 +01:00
Ricki Hirner
5374afb7ae Version bump to 4.2.6 2022-12-10 13:17:59 +01:00
Álvaro Brey
9ceee1b6c0 LoginSetupActivity: allow http/https in intent filter (#211)
* LoginSetupActivity: allow http/https in intent filter

Initially introduced in 054f1ece61,
this `intent-filter` breaks login from the Nextcloud app, as we send the server URI as `http`/`https`.

Signed-off-by: Álvaro Brey <alvaro.brey@nextcloud.com>

* Update intent-filter for login flow on api lvl 33 (closes bitfireAT/davx5#172)

Signed-off-by: Álvaro Brey <alvaro.brey@nextcloud.com>
Co-authored-by: Sunik Kupfer <kupfer@bitfire.at>
2022-12-08 17:48:13 +01:00
Sunik Kupfer
e6ecc7a9b0 Remove forced wifi-only sync when Data Saver is active (bitfireAT/davx5#171)
* Remove forced wifi-only sync when Data Saver is active

* move data saver warning to AppWarningsManager

* Minor changes

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2022-12-08 11:56:47 +01:00
Sunik Kupfer
1b17124b80 move warning notification logic into a warning class (bitfireAT/davx5#170)
* move warning notification logic into a warning class

* Move Warnings to UI package

* Minor changes

* Move "global sync disabled" warning to AccountListFragment, too

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2022-12-08 11:52:56 +01:00
Sunik Kupfer
02885947da Refactor DavService to WorkManager (bitfireAT/#164)
* SyncManager: remove retry intent
* refactor DavService to RefreshCollectionsWorker now using WorkManager
* move DavResourceFinder to new service detection package
* Optimize imports

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2022-12-08 11:52:38 +01:00
Ricki Hirner
3f2fcda6d3 Add Github sponsoring 2022-11-30 10:25:15 +01:00
Sunik Kupfer
ba46c4793a Update F-Droid changelog 2022-11-29 15:13:24 +01:00
Sunik Kupfer
405ef4d0b8 Version bump to 4.2.5 2022-11-29 15:09:24 +01:00
Sunik Kupfer
5e099561c1 Fetch translations from Transifex 2022-11-29 15:07:22 +01:00
Ricki Hirner
dd9ac77705 Add referrer with utm_source to task app installation Intents 2022-11-29 15:04:34 +01:00
Ricki Hirner
369d5e49f1 Update dependencies, version bump to 4.2.5-rc.1 2022-11-24 18:14:26 +01:00
Sunik Kupfer
5222c13a23 Support read-only task lists for Tasks.org (bitfireAT/davx5#160)
* [WIP] save task list access level to android task list for tasks.org to use
* Disrecommend using OpenTasks in selection dialog
* Update string, use matching ical4android

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2022-11-24 17:33:37 +01:00
Ricki Hirner
b17b38ff50 Update dependencies, including ical4android 2022-11-09 13:16:59 +01:00
Ricki Hirner
3b74ad5257 PermissionsFragment: Hide notification permission switch on Android < 13 2022-11-04 17:53:30 +01:00
Clément GIBAUD
e4a5c47887 Add framagenda to pre-set URLs (#192) 2022-11-04 16:31:24 +01:00
Sunik Kupfer
8915a3c103 android 13 compatibility (bitfireAT/davx5#155)
* check for notify permission before notifying

* [WIP] add notification permission request

* Revert "check for notify permission before notifying"

This reverts commit ed8e046d73163bef5684622bd877f08b9ee781c6.

* add notification permission request

* [WIP] add notifications disabled card in accounts view

* add notifications disabled card in accounts view

* reorder permissions

* use dp instead of mm for image max height in permissions view

* stop using array to bundle notification permissions

* Use new permissions contract for PermissionsFragment

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2022-11-01 12:51:20 +01:00
Ricki Hirner
43ae3fcd1f Update libraries 2022-10-29 16:42:05 +02:00
Ricki Hirner
796eddb975 Update dav4jvm, Kotlin 2022-10-28 13:34:32 +02:00
Ricki Hirner
57a77bd5f5 Actually update vcard4android (better read-only contacts support) 2022-10-28 13:15:38 +02:00
Sunik Kupfer
d32b21a465 Update F-Droid changelog 2022-10-25 11:22:34 +02:00
Sunik Kupfer
31a61f508d Version bump to 4.2.4 2022-10-25 11:16:53 +02:00
Ricki Hirner
a8e21bed8b Force downloading all contacts after resetting read-only contacts when collection supports Collection Sync 2022-10-21 13:41:22 +02:00
Ricki Hirner
7b70cf45fc Fetch translations from Transifex 2022-10-21 13:41:22 +02:00
Ricki Hirner
66282c10e6 Version bump to 4.2.4-rc.1 2022-10-21 13:41:22 +02:00
Ricki Hirner
a493f0a9a9 Update dependencies 2022-10-21 13:41:22 +02:00
Ricki Hirner
951c29e66a Force sync after resetting read-only contacts (bitfireAT/davx5#154)
* Force sync after resetting read-only contacts

* Rename variable for clarity
2022-10-21 13:41:21 +02:00
Sunik Kupfer
0f124cf5b5 LocalAddressBook: update read-only flag of groups, too (bitfireAT/davx5#153)
* LocalAddressBook: update read-only flag of groups, too

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2022-10-21 13:41:21 +02:00
Sunik Kupfer
9bd564b2c6 fix crash when accounts vanish (closes bitfireAT/davx5#140) (bitfireAT/davx5#148) 2022-10-21 13:41:21 +02:00
Ricki Hirner
bec9f8bcfb Fetch translations from Transifex (including new translations) 2022-10-14 23:06:56 +02:00
Ricki Hirner
d2614bd87e Create an option to pre-select the read-only setting for address books (bitfireAT/davx5#141)
* [WIP] add managed restriction to force read-only addressbooks
* Honor app-wide read-only address book setting when syncing address books
* reflect status of force read-only address books setting in the GUI

Co-authored-by: Sunik Kupfer <kupfer@bitfire.at>
2022-10-12 17:35:12 +02:00
Ricki Hirner
e14460111e Use Material switches for preferences 2022-10-11 12:48:16 +02:00
Ricki Hirner
31fb7ec190 CalDAV/CardDAV UI: replace checkbox by switch (bitfireAT/davx5#143)
Closes bitfireAT/davx5#142
2022-10-11 10:15:01 +02:00
Ricki Hirner
99710ca94e Update Manual URL 2022-10-08 18:58:31 +02:00
Ricki Hirner
c635dc8aa3 Add pre-set base URLs 2022-10-03 10:51:33 +02:00
Ricki Hirner
ce3f3b7cc4 Update vcard4android (disable LABEL generation); version bump to 4.2.4-beta.1 2022-10-03 10:51:12 +02:00
Ricki Hirner
2b16cd6365 Lint 2022-09-27 13:03:08 +02:00
Ricki Hirner
8a7f51b705 Update dependencies, compile SDK level, build tools, gradle plugin 2022-09-27 13:03:08 +02:00
Ricki Hirner
f5b08b60bf Update ical4j (handle NPE when time zone is known to Android but not to ical4j) 2022-09-27 13:03:06 +02:00
Ricki Hirner
fc819eb001 Update README
- closes #153
- closes #152
2022-09-27 11:33:07 +02:00
Ricki Hirner
fbbf2a6b07 Add calendar and contacts provider to manifest/queries (for increased compatibility with apps like Huawei calendar) 2022-09-23 13:32:22 +02:00
Ricki Hirner
8ff90954cd Don't run AccountsUpdatedListener while account is being renamed
Don't run AccountsUpdatedListener while account is being renamed (closes bitfireAT/davx5#135)
2022-09-22 20:55:08 +02:00
Sandelinos
a7fcc087bc Add monochrome icon (#151) 2022-09-20 20:32:58 +02:00
Ricki Hirner
e2c2e566b9 Bump version to 4.2.3.4 2022-09-20 18:50:24 +02:00
Ricki Hirner
d4eaa0718a Fetch translations from Transifex 2022-09-19 16:36:52 +02:00
Ricki Hirner
e3c44f2e50 Update ical4android (enable relaxed valiation of email addresses); bump version to 4.2.3.4-rc.2 2022-09-19 16:35:02 +02:00
Ricki Hirner
c517c9c3b5 Bump version to 4.2.3.4-rc1 2022-09-19 16:35:01 +02:00
Ricki Hirner
757c01c186 Update dependencies (including ical4android to address Kiev/Kyiv bug)
See https://github.com/bitfireAT/davx5-ose/discussions/133
2022-09-19 16:35:01 +02:00
Ricki Hirner
97eb45ec12 Fetch translations from Transifex 2022-09-08 22:04:35 +02:00
Ricki Hirner
d7102f94bd Version bump to 4.2.3.3 2022-09-08 22:03:43 +02:00
Ricki Hirner
cf76cc16ce Update ical4android 2022-09-08 22:03:43 +02:00
Sunik Kupfer
78e4cbe06d roll back commons-io to 2.8.0 for API levels <26 (closes bitfireAT/davx5#130) 2022-09-07 12:23:12 +02:00
Ricki Hirner
f47adbef4a Wrap GrantPermissionRule into InitCalendarProviderRule
Closes bitfireAT/davx5#127
2022-08-31 17:16:56 +02:00
Sunik Kupfer
94c9f869c9 Generate new filename if invalid for upload (closes bitfireAT/davx5#110)
* use random UUID instead of UID as upload file name if UID would be dangerous

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2022-08-31 17:16:54 +02:00
Ricki Hirner
ee993a8557 Rename "standard" flavor to "ose" 2022-08-31 17:12:25 +02:00
Ricki Hirner
03deb4191a Bump version to 4.2.3.2 2022-08-29 15:07:51 +02:00
Ricki Hirner
b5c16ddbd4 Fetch translations from Transifex 2022-08-29 15:07:50 +02:00
Ricki Hirner
f054ab43b9 Update ical4android 2022-08-29 15:07:49 +02:00
Ricki Hirner
aeb4541eb9 Version bump to 4.2.3.1 2022-08-24 16:56:19 +02:00
Ricki Hirner
7144b963e4 Version bump to 4.2.3.1-rc1 2022-08-23 22:00:08 +02:00
Ricki Hirner
f38e7b2dab Update hilt and ical4android (ical4android had dependency that caused JSONObject problem in release builds) 2022-08-23 21:51:54 +02:00
Ricki Hirner
62912dc83a Add F-Droid changelog 2022-08-23 12:01:17 +02:00
Ricki Hirner
acc3ca1b4c Version bump to 4.2.3 2022-08-23 11:36:32 +02:00
Ricki Hirner
fec8eae9d0 Update dependencies, including ical4android 2022-08-19 12:39:32 +02:00
Ricki Hirner
885035fadf Update dependencies 2022-08-19 12:39:32 +02:00
Sunik Kupfer
e5604118fd confirmation dialog for removing webdav mount (closes bitfireAT/davx5#47) (#119)
* confirmation dialog for removing webdav mount

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2022-08-19 12:39:32 +02:00
Ricki Hirner
20e79e49b3 Rename notification for external file logging 2022-08-15 12:41:50 +02:00
Ricki Hirner
b3e0cef0a6 Fix Android10ResolverTest when IPv6 is the primary address type 2022-08-15 12:41:49 +02:00
Michael Biebl
7bb97ffa31 Remove unused colors (#110)
They are a left over from
commit 12802f8697 and the incomplete
removal in commit 054f1ece61
2022-08-10 17:09:35 +02:00
Ricki Hirner
fbb7c6d161 Update dav4jvm, gradle plugin 2022-08-08 20:52:03 +02:00
Ricki Hirner
4f143318da Update dependencies and ical4android 2022-07-28 15:12:34 +02:00
Ricki Hirner
8aefab65f6 Update dependencies 2022-07-28 15:01:23 +02:00
Ricki Hirner
38c40d9466 Add authors 2022-06-21 14:45:14 +02:00
Sunik Kupfer
5daae1b74d Update F-Droid changelog 2022-06-21 12:50:25 +02:00
Sunik Kupfer
8c22b4ed3b Version bump to 4.2.2 2022-06-21 12:35:05 +02:00
Sunik Kupfer
dc94791637 etch translations from Transifex 2022-06-21 12:09:04 +02:00
Sunik Kupfer
d2c0a2bb44 use findActivity() provided by hilt (closes bitfireAT/davx5#106) (#107)
* use FragmentComponentManagers findActivity method provided by hilt (closes bitfireAT/davx5#106)

* WebCalFragment CalendarViewHolder: Pass Fragment instead of FragmentManager so that requireActivity() can be used

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2022-06-21 12:05:43 +02:00
Patrick Lang
c90e0bf085 Removed update related to from JtxSyncManager (#92)
* Removed update related to from JtxSyncManager

updateRelatedTo is now done implicitely in the sync content provider for every inserted or updated icalobject/relatedto

* Update ical4android

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2022-06-21 12:04:12 +02:00
Sunik Kupfer
2e6ce88c58 non-flaky tests (#103)
* [WIP] initialization code to make tests non-flaky

* init code as junit rule and remove flaky annotations

* remove exception for flaky tests in Github test workflow

* ensure correct class rule execution order
2022-06-21 12:02:28 +02:00
Patrick Lang
5b0788b2e9 Don't set account visibility if it was already visible (#102) 2022-06-21 11:55:59 +02:00
Ricki Hirner
f813392c72 Version bump to 4.2.1.2-alpha.3 2022-05-30 14:49:47 +02:00
Ricki Hirner
bd7d6c7b4d AccountSettings: handle address book accounts (#101)
* AccountSettings: handle address book accounts (fixes: address books are not synced because of IllegalArgumentException)

* Tests (not finished)
2022-05-30 14:49:47 +02:00
Ricki Hirner
0e93a47d6d Version bump to 4.2.1.2-alpha.2 2022-05-29 15:03:37 +02:00
Ricki Hirner
99325e8930 Sync adapter: improve HttpClient and cancellation (#100)
* Sync adapter: share single HttpClient between all SyncManagers (should fix bitfireAT/davx5#99)
* HttpClient: use producer for CertManager
* Call setAccountVisibility only when necessary
* Fix tests
2022-05-29 15:03:36 +02:00
Ricki Hirner
0a11a0bcfc Don't set up periodic syncs for contacts sync of the address book acc… (#97)
Don't set up periodic syncs for contacts sync of the address book accounts (fixes bitfireAT/davx5#96)
2022-05-29 15:02:17 +02:00
Ricki Hirner
f3c79367ef Version bump to 4.2.1.2-alpha.1 2022-05-29 15:01:57 +02:00
Ricki Hirner
25ea4fc2ca Create SECURITY.md 2022-05-28 20:06:35 +02:00
Ricki Hirner
4dfebe4355 Hilt: use intro OpenSourceFragment 2022-05-28 19:19:43 +02:00
Ricki Hirner
b51d18a659 Use Hilt instead of Koin (#93)
* Use Hilt for DI
* Use Hilt for settings providers
* Use Hilt for login credentials fragment
* Use Hilt instead of ServiceLoader for intro fragments
* Rewrite from Koin to Hilt
* Tests
* Use more Hilt modules for service loading (account drawer, intro fragments)
* Lint
* WebDAV provider: don't listen to changes in onCreate (causes problems with Hilt and tests)
2022-05-28 11:05:35 +02:00
Ricki Hirner
07ba158e66 Fix network security policy with tests 2022-05-27 14:36:25 +02:00
Patrick Lang
338d0d4691 Moved jtx Board in TaskProvider selection to first position (#94) 2022-05-27 14:35:59 +02:00
Bernhard Stockmann
7f58e3e4a8 Update images, add video URL (#49)
* Update images, add video URL

* Video without ab_channel
2022-05-26 12:05:11 +02:00
Michael Biebl
b71452195e Remove duplicate copyright header (#46) 2022-05-23 11:31:47 +02:00
Ricki Hirner
cf3be750d0 Fetch translations from Transifex 2022-05-22 23:50:22 +02:00
Ricki Hirner
212392e78c Version bump to 4.2.1.1 2022-05-22 23:49:45 +02:00
Sunik Kupfer
ddae93c225 Add mocck dependency for tests 2022-05-22 23:48:00 +02:00
Ricki Hirner
1f4a25e4a4 Use Koin for all tests; CI: run tests for all flavors (#91)
Closes bitfireAT/davx5#89
2022-05-22 18:56:47 +02:00
Ricki Hirner
84f55bc39a Fix Android10Resolver test (requires Android 10 instead of 8) 2022-05-12 10:46:49 +02:00
Ricki Hirner
8384730aff Refactor some models to use proper constructors instead of custom initialize() methods 2022-05-12 10:46:49 +02:00
Ricki Hirner
8ca3d857d3 Use Koin Dependency Injection for Singletons (#84) 2022-05-12 10:46:48 +02:00
Ricki Hirner
12cc2cfcde Update submodules 2022-05-12 10:39:28 +02:00
Ricki Hirner
7d424183a8 Update gradle plugin, Kotlin, AndroidX Material 2022-05-12 10:39:26 +02:00
Ricki Hirner
d3c10c36b0 CI: create Github release for new tags 2022-05-05 15:21:57 +02:00
Ricki Hirner
f37f3e5104 Bump version to 4.2.1 2022-05-05 14:25:38 +02:00
Ricki Hirner
541f51f77e Version bump to 4.2.1-rc.2 2022-04-29 16:03:59 +02:00
Ricki Hirner
9bc4b0a837 Fetch translations from Transifex 2022-04-25 14:52:53 +02:00
Ricki Hirner
f31c3cab3d Move EarnBadgesActivity strings to gplay 2022-04-25 14:50:35 +02:00
Ricki Hirner
776adedfbf Version bump to 4.2.1-rc.1 2022-04-25 14:25:36 +02:00
Ricki Hirner
054f1ece61 Setup intent filter (#80)
* Add intent filter for caldav(s)://, carddav(s):// and davx5:// schemes
* Define intent-filter only for standard and gplay flavors
* Merge manifest; remove Espresso tests (further tests should be added)
* Lint

Closes bitfireAT/davx5#77
2022-04-25 14:25:02 +02:00
Ricki Hirner
eaea4936d7 ical4android: don't crash on RDATEs with PERIOD 2022-04-25 14:20:51 +02:00
Ricki Hirner
6b98088922 Update dav4jvm 2022-04-24 10:38:17 +02:00
Ricki Hirner
5d61487a85 Rename at.bitfire.davdroid.model to at.bitfire.davdroid.db to make clear it contains database models (and not view models) 2022-04-24 10:38:17 +02:00
Ricki Hirner
d4b82d37be Version bump to 4.2.1-beta.2 2022-04-21 21:18:32 +02:00
Ricki Hirner
ffea72bc7c Update dav4jvm for improved service compatibility 2022-04-21 21:18:08 +02:00
Ricki Hirner
f4b157a101 Server handles only tasks not jouranls (#79)
* Service detection: update properties of already existing collections which are not in homesets
* Trigger service detection after creating a collection because the server may change properties
* jtx sync: query tasks/journals only when supported

Should fix bitfireAT/davx5#78
2022-04-20 17:57:11 +02:00
Ricki Hirner
c283691399 jtx Board sync: show whether VTODO and/or VJOURNAL components are supported by a collection; update ical4android 2022-04-20 17:57:09 +02:00
Ricki Hirner
e2c48df68f Version bump to 4.2.1-beta.1 2022-04-15 12:22:42 +02:00
Ricki Hirner
40941f9bce Move file, update gradle and plugin 2022-04-15 12:12:17 +02:00
Sunik Kupfer
12802f8697 Mirror changes in AccountsDrawerHandler
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2022-04-15 12:01:09 +02:00
Michael Biebl
3c0a26bb0c Preferences (#25)
* Set InputType for proxy_host and proxy_port programmatically

Since AndroidX it is no longer sufficient to simply specify
android:inputType in settings_app.xml.

* Drop custom IntEditTextPreference.kt

Doesn't appear to be necessary anymore.
2022-04-11 11:24:24 +02:00
Ricki Hirner
08dfa3f048 Fetch translations from Transifex 2022-03-23 18:09:47 +01:00
Ricki Hirner
cd4aa0891b ical4android fix; raise SDK level to 32; bump version to 4.2.0.3 2022-03-23 18:09:02 +01:00
Patrick Lang
c8723d0f64 propagate privWriteContent as read only collection (#71) 2022-03-23 18:08:34 +01:00
Ricki Hirner
5ee6e57b03 Fetch translations from Transifex 2022-03-22 13:10:56 +01:00
Ricki Hirner
48bd99e0b6 Version bump to 4.2.0.2 2022-03-22 13:05:23 +01:00
Ricki Hirner
ab806e458a vcard4android: contact photo improvements 2022-03-22 12:55:42 +01:00
Ricki Hirner
2c785684a2 Navigation drawer: update Community 2022-03-20 20:26:45 +01:00
Ricki Hirner
522fc8763f Fetch translations from Transifex 2022-03-20 20:26:17 +01:00
Ricki Hirner
df08cf6074 Version bump to 4.2.0.1 2022-03-20 20:25:32 +01:00
Ricki Hirner
763da2221c Contact photos: always output JPEG (vcard4android); sync: query max-resource-size (at the moment only for debugging) 2022-03-20 20:25:14 +01:00
Ricki Hirner
850b1f54a8 vcard4android: better handling of (invalid) photos; reduce RAM usage by reducing sync threads a bit 2022-03-19 18:44:53 +01:00
Ricki Hirner
f24c591283 F-Droid changelogs 2022-03-18 11:05:04 +01:00
Ricki Hirner
b8d83cf46f Fetch translations from Transifex 2022-03-18 10:36:21 +01:00
Ricki Hirner
ec7d59a46b Version bump to 4.2 2022-03-18 10:36:05 +01:00
Ricki Hirner
66ebe4f224 Update dav4jvm (fixes bitfireAT/davx5#69) 2022-03-18 10:35:44 +01:00
Ricki Hirner
abb60a4331 Fetch translations from Transifex 2022-03-16 14:43:44 +01:00
Ricki Hirner
4950a2390d Version bump to 4.2-rc.1 2022-03-16 14:43:43 +01:00
Ricki Hirner
49ba32e64f Update dependencies 2022-03-16 14:43:43 +01:00
Ricki Hirner
da5ff1ea39 Move OnAccountsUpdateListener to App (may fix bitfireAT/davx5#45) 2022-03-16 14:43:43 +01:00
Ricki Hirner
da4b5cf143 DebugInfo: provide IntentBuilder that truncates large logs/local resource dumps (should fix bitfireAT/davx5#69) 2022-03-16 14:43:42 +01:00
Ricki Hirner
248dd6fdb3 Update dependencies (including vcard4android with new photo handling) 2022-03-16 14:43:42 +01:00
Ricki Hirner
979123b9ef Add option to select detected collections for synchronization automatically 2022-03-16 14:43:37 +01:00
Sunik Kupfer
7367477ebe Add option to use socks proxy for orbot (closes bitfireAT/davx5#28) (#64)
* Add option to use SOCKS proxy for Orbot
* Fix tests, add constants
* Add option for "no proxy", add migration
* Improve UI

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2022-03-09 12:51:31 +01:00
Ricki Hirner
8fb9b3a86e Update dependencies; bump version to 4.2.0-beta.1 2022-03-06 11:47:39 +01:00
Sunik Kupfer
a7779ca6c3 billing and play dependencies (bitfireAT/davx5#62) (#63)
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2022-03-06 11:47:29 +01:00
Ricki Hirner
73a7b718e2 Update dependencies; version bump to 4.2.0-alpha.3 2022-03-06 11:44:12 +01:00
Patrick Lang
4084dc36a2 Updated queryCapabilities to use the same code as in TasksSyncManager, added try-catch block for sync-method (#60)
* Updated queryCapabilities to use the same code as in TasksSyncManager
Might solve #59

* Added try-catch block for sync-method
updated min version for JtxBoard in TaskProvider.kt
Should solve #59

* removed syncResult.databaseError = true

* Changed wrong file before

* Created TasksSyncUtils to put notifyProviderTooOld method

* Cleaned up imports

* Added Version check in JtxSyncAdapterService.kt, required TaskProvider.checkVersion to be public

* Minor changes

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2022-03-06 11:41:33 +01:00
Ricki Hirner
7534fe689b Update ical4android 2022-03-06 11:41:33 +01:00
Ricki Hirner
955c09d518 Update gradle plugin and build tools 2022-03-06 11:41:28 +01:00
Ricki Hirner
7493d1eda8 Version bump to 4.2.0-alpha.2 2022-02-24 11:15:42 +01:00
Ricki Hirner
ff3b5f50af Update ical4android to fix problem with Jtx Board relations 2022-02-24 11:15:26 +01:00
Ricki Hirner
1554c6ae1c Version bump to 4.2.0-alpha.1; update dependencies 2022-02-09 13:43:57 +01:00
Patrick Lang
16311708f8 Jtx Board synchronization (#56)
Support tasks and notes synchronization with Jtx Board

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2022-02-09 13:43:17 +01:00
Ricki Hirner
6ab291bdb2 Update gradle plugin 2022-02-09 13:42:37 +01:00
Ricki Hirner
19be3c34bd Version bump to 4.1.1; add F-Droid changelog 2022-02-01 11:00:55 +01:00
Ricki Hirner
64bd373611 Fetch translations from Transifex 2022-02-01 10:46:14 +01:00
Ricki Hirner
b2a5a1b1f3 Version bump to 4.1.1-rc.1 2022-01-28 17:06:26 +01:00
Ricki Hirner
6af033a351 Avoid UninitializedPropertyAccessException when accessing collectionURL for logging the sync 2022-01-27 14:11:20 +01:00
Ricki Hirner
1532fab49e Use asSyncAdapter() for calendar provider URIs 2022-01-27 12:18:09 +01:00
Ricki Hirner
62af6f45ab Update gradle plugin 2022-01-27 12:18:09 +01:00
Ricki Hirner
2ec33002c8 Version bump to 4.1.1-beta1 2022-01-24 11:23:51 +01:00
Ricki Hirner
81aa95223f Fetch translations from Transifex 2022-01-24 11:14:50 +01:00
Ricki Hirner
c1125a5bd0 Update dependencies 2022-01-24 11:14:22 +01:00
Sunik Kupfer
7593229623 Delete dirty events without instances before syncing
Delete dirty events without instances before syncing (resolves bitfireAT/davx5#34)

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2022-01-22 10:55:32 +01:00
Ricki Hirner
87725a3b5d Show a warning when disk space is low, which will stop synchronization (resolves bitfireAT/davx5#44) 2022-01-17 18:22:45 +01:00
Ricki Hirner
708c80fa0a Update cert4android and vcard4android (only copyright) 2022-01-13 14:18:23 +01:00
Ricki Hirner
6c34737cbc Fix that foreground service notification was always shown (fix for bitfireAT/davx5#32) 2022-01-13 14:18:23 +01:00
Sunik Kupfer
989cb1fd99 Fix foreground service start not allowed exception (resolves bitfireAT/davx5#32)
* Make "Battery optimization whitelisting" a global DAVx5 setting
* Make "Keep in foreground" setting dependent of "Battery optimization whitelisting" setting
* if foreground service is enabled, remind user to enable battery optimization whitelisting too, before starting the foreground service

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
2022-01-13 14:18:23 +01:00
Sunik Kupfer
c9b88b0c73 Update ical4j (Closes bitfireAT/davx5#37) (#43) 2022-01-13 14:18:23 +01:00
Ricki Hirner
161157a37c Fetch translations from Transifex 2022-01-05 16:20:12 +01:00
sunkup
9ac05f4f51 Add warning to avoid apostrophes in account names
Add warning to avoid apostrophes in account names. Resolves bitfireAT/davx5#5
2022-01-05 16:18:45 +01:00
sunkup
ed08cc8d15 update dependencies (#33) 2022-01-04 12:38:01 +01:00
Ricki Hirner
197432a245 README: Add Twitter and Website 2021-12-30 19:22:59 +01:00
Ricki Hirner
32d215b3a4 Add F-Droid changelog 2021-12-26 13:41:42 +01:00
Ricki Hirner
d08a1ec737 Version bump to 4.1; update vcard4android 2021-12-20 17:48:13 +01:00
Ricki Hirner
ccd738b4ff Update vcard4android: always add FN (also for vCard4) 2021-12-20 17:47:57 +01:00
Ricki Hirner
985dcf09d3 Fetch translations from Transifex 2021-12-16 13:37:18 +01:00
Ricki Hirner
2b940bfcd7 Version bump to 4.1-rc.1 2021-12-16 13:36:55 +01:00
Ricki Hirner
22e4c75064 WebDAV mounts: add empty view manual link (resolves bitfireAT/davx5#29); update dependencies 2021-12-16 13:36:38 +01:00
Ricki Hirner
c7fd893b31 Update copyright (resolves bitfireAT/davx5#26) 2021-12-15 17:55:00 +01:00
Ricki Hirner
ac7e717cde WebDAV mounts activity: add Help icon 2021-12-15 17:48:30 +01:00
Ricki Hirner
0009429b68 Update dependencies 2021-12-12 16:50:53 +01:00
Ricki Hirner
4b49df77ed Update room, version bump to 4.1-alpha.3 2021-12-03 16:57:09 +01:00
Ricki Hirner
3c6d1172cf Optimize singletons and companion objects (resolves bitfireAT/davx5#25) 2021-12-03 16:57:09 +01:00
Ricki Hirner
6960a48021 Change static singletons to cached singletons with weak references to save memory when the objects are not needed anymore 2021-12-03 15:46:29 +01:00
Ricki Hirner
54d1b5ec2e Refactor WebDAV cache 2021-12-03 15:46:29 +01:00
Ricki Hirner
99ecfb1ca3 WebDAV mounts: add empty view; add navigation drawer entry for Managed DAVx5 (closes bitfireAT/davx5#23) 2021-12-03 15:46:09 +01:00
Ricki Hirner
f1a441c27d WebDAV: accept compliance class 2 or 3 (closes bitfireAT/davx5#21); fix division by zero in progress
- WebDAV service detection: accept compliance class 2 or 3, because they both imply class 1 (there are servers which don't show class 1 although they MUST)
- WebDAV random access: fix division by zero when file size reported by HEAD is zero
2021-12-03 15:45:31 +01:00
Ricki Hirner
56c7d2d73f Remove dokka because the generated documentation isn't used by anybody; fix dav4jvm reference 2021-11-27 18:07:12 +01:00
Ricki Hirner
be98bef4ab Sync adapter: add tests, move check of simultaneous running syncs to new ConcurrentUtils 2021-11-23 14:34:25 +01:00
Ricki Hirner
1747cb2896 Update dependencies 2021-11-23 14:31:34 +01:00
Ricki Hirner
c82d5cb1b4 WebcalModel: get rid of extra WorkerThread + Handler (were not stopped when fragment was closed) 2021-11-22 19:48:28 +01:00
Ricki Hirner
f1de88a0f1 Android10Resolver: add test 2021-11-19 22:45:52 +01:00
Ricki Hirner
a280de73a3 Update cert4android and vcard4android (only README changes); Android10Resolver: use empty JVM object instead of UUID as handle 2021-11-19 14:41:32 +01:00
Michael Biebl
489e09ea08 Drop AboutInfo override for dav4jvm
With the recent move to GitHub, the AboutInfo override for dav4jvm was
broken as it still used com_gitlab_bitfireAT__dav4jvm whereas it is now:
```
$ ./gradlew findLibraries | grep dav4jvm
bitfireAT/dav4jvm (f1d9354a41) -> com_github_bitfireAT__dav4jvm
Manually requested license: mpl_2_0
```

Since the autogenerated dav4jvm AboutInfo looks fine (see attached
screenshot), remove the override.
2021-11-19 11:58:08 +01:00
Michael Biebl
3403992c8b Fix cert4android project URL
Follow-up for commit 331c102992
2021-11-19 11:52:40 +01:00
Ricki Hirner
9418b4f2a5 Temporarily disable jCard because of nextcloud/server#29693, version bump to 4.1-alpha.3 2021-11-13 13:56:42 +01:00
Ricki Hirner
26b4824124 Small Android10Resolver improvements 2021-11-13 13:56:19 +01:00
Ricki Hirner
2eeb1be833 DiskCacheTest: fixes another possible reason why tests may fail?
(trim deletes the files in modification time order, so if two files have the same
modification time, the order is not well-defined)
2021-11-13 11:09:50 +01:00
Ricki Hirner
db77f43931 Bump version to 4.1-alpha.2 2021-11-12 22:23:36 +01:00
Ricki Hirner
d96e311e8b Improve SRV record resolving (resolves bitfireAT/davx5#18)
- Android 8 to 10: use all DNS servers; prioritize resolvers of active connections
  (before: use only DNS servers of "active" connection, which may be a VPN connection without servers)
- Android 10+: use new DnsResolver API so that we don't need to know DNS servers anymore (also supports DoT etc.)
2021-11-12 22:23:09 +01:00
Ricki Hirner
952a10fb3d WebDAV cache: fix problem that sometimes the tests fail 2021-11-12 01:13:03 +01:00
Ricki Hirner
09384948b5 Github issues: add link to forum; add sponsorships 2021-11-10 13:02:48 +01:00
Ricki Hirner
0a17169a8b Update gradle and dependencies 2021-11-08 09:56:14 +01:00
Ricki Hirner
331c102992 Move to Github 2021-11-07 21:18:08 +01:00
Ricki Hirner
5de0718210 Update dependencies (including dav4jvm on Github instead of Gitlab) 2021-11-06 10:17:55 +01:00
Ricki Hirner
2504a81024 jCard: check MIME type from server; use MediaType for MIME types when possible 2021-11-05 01:38:59 +01:00
Ricki Hirner
ede9e1cf55 CI: use container from ghcr.io 2021-11-04 10:30:35 +01:00
Michael Biebl
cd1b14889b Properly cancel NOTIFY_REFRESH_COLLECTIONS error notifications
When displaying the notification, we use serviceId as tag, so use
serviceId as well when cancelling it.
2021-10-22 12:56:36 +00:00
Ricki Hirner
d8fa5d4979 Version bump to 4.1-alpha.1 2021-10-22 14:52:05 +02:00
Ricki Hirner
563b131211 Support jCard (closes #11) 2021-10-22 14:51:41 +02:00
Ricki Hirner
201719af1a Debug info: show available/total storage memory (closes #10) 2021-10-21 07:10:47 +02:00
Ricki Hirner
290e73229b Update dependencies 2021-10-17 16:06:15 +02:00
Ricki Hirner
265d899d2c WebDAV: always return MIME type; change cached file size/modification time after successful upload 2021-10-15 12:38:18 +02:00
Ricki Hirner
7d4a5863b3 Update gradle plugin 2021-10-14 18:16:06 +02:00
Ricki Hirner
265d75b290 Add F-Droid release notes 2021-10-11 16:57:50 +02:00
Ricki Hirner
918c2647da Version bump to 4.0 2021-10-10 13:53:45 +02:00
Ricki Hirner
1bf66770ef Version bump to 4.0-beta3 2021-10-07 21:50:19 +02:00
Ricki Hirner
75bd04e882 CardDAV KIND/MEMBER: always download group vCards after uploading them to keep groups in sync with server 2021-10-07 21:50:04 +02:00
Ricki Hirner
566d10ef8d Add time zone to debug info 2021-10-06 13:57:22 +02:00
Ricki Hirner
a10b1153ea Improve group assignment 2021-10-05 12:29:10 +02:00
Ricki Hirner
5b6a0a8114 Comments, version bump to 4.0-beta3 2021-10-05 10:19:39 +02:00
Ricki Hirner
e8fb6becf9 Fix Room crash and group processing performance problem 2021-10-05 10:08:14 +02:00
Ricki Hirner
f7eea633fb WebDAV: fix random access without DocumentState; add tests 2021-10-04 19:09:47 +02:00
Ricki Hirner
e22928eeea Fetch translations from Transifex 2021-10-04 17:42:43 +02:00
Ricki Hirner
be63957507 Version bump to 4.0-beta2 2021-10-04 17:42:43 +02:00
Ricki Hirner
764102ae6e vCard4-groups: apply pending memberships after a contact has been downloaded again 2021-10-04 14:35:31 +02:00
Ricki Hirner
459cb538d7 GroupMembershipHandler: ignore memberships in groups that don't exist (anymore) 2021-10-04 11:20:19 +02:00
Ricki Hirner
9ee2930312 WebDAV: Add permanent notification to hopefully avoid being killed because of "excessive cpu usage" 2021-10-04 11:08:50 +02:00
Ricki Hirner
5161fad251 WebDAV: optimize cache 2021-10-03 12:51:33 +02:00
Ricki Hirner
a42517e1ed Improve WebDAV UI 2021-10-02 18:54:43 +02:00
Ricki Hirner
231e54db4f Lint 2021-09-29 13:40:36 +02:00
Ricki Hirner
6e502ae8dc Squashed commit of the following:
commit 56f5f5a6cbad595534148a160678972734ca6cf2
Author: Ricki Hirner <hirner@bitfire.at>
Date:   Wed Sep 29 10:59:23 2021 +0200

    Version bump to 4.0-beta1

commit 2a4d7501d9d6cd572269de21e82c204c5a2a57e8
Author: Ricki Hirner <hirner@bitfire.at>
Date:   Tue Sep 28 21:45:49 2021 +0200

    Fix UiUtils.launchUri once again

commit 293accaf8f26842d198e9d5c4fc9029cb6ac71cc
Author: Ricki Hirner <hirner@bitfire.at>
Date:   Tue Sep 28 13:47:06 2021 +0200

    Adjust gradle daemon heap size

commit 7763a51c9bbc7baee36ef71648889f7fb4fe965d
Author: Ricki Hirner <hirner@bitfire.at>
Date:   Tue Sep 28 12:21:37 2021 +0200

    Query and show quota

commit 95f4dbfd8ffadd930aab9fc36dc3865bfee54fb7
Author: Ricki Hirner <hirner@bitfire.at>
Date:   Mon Sep 27 22:16:48 2021 +0200

    Persist cookies over WebDAV sessions

commit 17c9327eb3ef3d9384b28b70aa9637718d34e8a2
Author: Ricki Hirner <hirner@bitfire.at>
Date:   Sun Sep 26 19:54:57 2021 +0200

    Add WebDAV caches

    * add random access page cache
    * add thumbnail cache
    * add HEAD response cache

commit 0e5237f8b178ab66099999f72e657c59c9302d71
Author: Ricki Hirner <hirner@bitfire.at>
Date:   Tue Aug 31 13:25:36 2021 +0200

    Add WebDAV Access over Storage Access Framework
2021-09-29 11:04:44 +02:00
Ricki Hirner
da7d1e1ec6 UiUtils: fix wrong "Please install a browser" message 2021-09-29 11:04:00 +02:00
Ricki Hirner
1a13fbbff4 ical4android: fix attendee problem; version bump to 3.4.1-beta1 2021-09-11 15:02:03 +02:00
Ricki Hirner
425a88f6f7 Fetch translations from Transifex 2021-09-09 19:31:34 +02:00
Ricki Hirner
3afc7c8dbc Version bump to 3.4.0.1 2021-09-09 19:31:17 +02:00
Ricki Hirner
a6854f2461 Merge branch 'dev-3.x-ose' of gitlab.com:bitfireAT/davx5-ose into dev-3.x-ose 2021-09-09 16:35:19 +02:00
Ricki Hirner
fc574f5853 lint 2021-09-09 16:34:52 +02:00
rhi
12a85cbf60 Merge branch 'dev-3.x-ose' into 'dev-3.x-ose'
add app icon to Fastlane

See merge request bitfireAT/davx5-ose!62
2021-09-08 07:56:14 +00:00
Ricki Hirner
e0e69d3ffd Collection list: migrate to Paging3 2021-09-07 23:04:28 +02:00
Hans-Christoph Steiner
17430ab9d1 add app icon to Fastlane 2021-09-07 17:25:07 +02:00
Ricki Hirner
3e77b3ab1b SyncManager syncRemote: use main dispatcher for outer coroutine 2021-09-01 18:03:02 +02:00
Ricki Hirner
700ced1701 Simplify checking of duplicated sync calls (don't use WeakReference but locks instead) 2021-09-01 18:03:02 +02:00
Ricki Hirner
d8a70ca30c ProGuard rules: XmlPullParser 2021-08-31 15:08:17 +02:00
Ricki Hirner
57f11f84c9 Update Gitlab CI 2021-08-31 14:24:03 +02:00
Ricki Hirner
660daf6573 Fetch translations from Transifex 2021-08-31 13:47:54 +02:00
Ricki Hirner
ad657dfad7 Version bump to 3.4 2021-08-31 13:47:19 +02:00
Ricki Hirner
bc8d9b5b8c Update Gitlab CI 2021-08-26 18:22:20 +02:00
Ricki Hirner
925bc6a7b0 Update dav4jvm; group strategy: passing LocalAddressBook instead of ContactSyncManager is enough 2021-08-26 18:22:20 +02:00
Ricki Hirner
514dfdae80 Location permission for SSID restriction: make clear that "precise" location is required in Android 12+ 2021-08-26 12:47:20 +02:00
Ricki Hirner
12a08b4a0b Update dependencies 2021-08-25 17:54:05 +02:00
Ricki Hirner
52c1a69ac6 Version bump to 3.4-beta3 2021-08-23 12:02:14 +02:00
Ricki Hirner
87fdb06fc9 Version bump to 3.4-beta2 2021-08-23 11:08:17 +02:00
Ricki Hirner
450368bd0e Use ThreadPoolExecutor with dynamic number of threads as global sync work dispatcher 2021-08-23 11:08:01 +02:00
Ricki Hirner
bf18d02715 Raise SDK level to 31 (Android 12); explicitly make components with intent-filters (not) exported 2021-08-23 11:08:01 +02:00
Ricki Hirner
43086f3cb9 Fetch translations from Transifex 2021-08-22 20:45:42 +02:00
Ricki Hirner
8973054e85 Version bump to 3.4-beta1 2021-08-22 20:44:48 +02:00
Ricki Hirner
8069657238 Fix tests 2021-08-22 18:50:36 +02:00
Ricki Hirner
9ecb02a059 Update SyncManager test 2021-08-22 15:12:33 +02:00
Ricki Hirner
5307333fcf CardDAV: add tests for new data handlers/builders (also relevant for contact groups) 2021-08-22 14:40:39 +02:00
Ricki Hirner
2700ac51da Refactor group handling 2021-08-22 14:40:39 +02:00
Ricki Hirner
7356a4152f Manage worker threads in Sync*Service, not in SyncManager 2021-08-22 14:40:39 +02:00
Ricki Hirner
1c356c1e17 Use strategies to handle contact groups (decouple from ContactsSyncManager) 2021-08-22 14:40:39 +02:00
Ricki Hirner
5feb9d658f Fetch translations from Transifex 2021-07-31 11:33:37 +02:00
Ricki Hirner
1259a8ae0d Version bump to 3.3.13-beta1 2021-07-31 11:33:01 +02:00
Ricki Hirner
6dd6833446 Better support for vCard contact dates and other properties (great rewrite of vcard4android) 2021-07-31 11:32:39 +02:00
Ricki Hirner
bf1d3ef624 Remove onClick from layout XMLs 2021-07-29 13:08:50 +02:00
Ricki Hirner
9d77b54d89 Update gradle 2021-07-29 11:16:46 +02:00
Ricki Hirner
e580dceb65 AboutActivity: update copyright string to mention contributors 2021-07-26 12:51:28 +02:00
Ricki Hirner
6a9f31d3f0 Use new Flexbox repo 2021-07-25 15:52:54 +02:00
Ricki Hirner
12c1d99a2e Update dependencies, including ical4android 2021-07-25 11:41:01 +02:00
Ricki Hirner
d8ab4ffddd Debug info: attach logcat by default; ical4android: don't crash on too large strings when dumping objects 2021-07-22 21:24:31 +02:00
Ricki Hirner
265d086301 Use ./fetch-translations instead of "tx pull" again (also fetches translators) 2021-07-22 16:02:07 +02:00
Ricki Hirner
3a1844d5b5 Transifex: use "tx pull" instead of scripts/fetch-translations.sh from now on 2021-07-20 13:19:49 +02:00
Ricki Hirner
189d7a31d8 Version bump to 3.3.12 2021-07-18 11:35:04 +02:00
Ricki Hirner
5e49d9c191 Version bump to 3.3.12-rc1 2021-07-16 01:51:55 +02:00
Ricki Hirner
f7a176513a Improve AutoComplete fields, especially in AppCompatDelegate-set dark mode 2021-07-16 01:50:37 +02:00
Ricki Hirner
0da5a967b9 Fetch translations from Transifex 2021-07-14 16:32:01 +02:00
Ricki Hirner
b39321d033 Update cert4android translations 2021-07-14 16:28:44 +02:00
Ricki Hirner
a84b8a5a65 Version bump to 3.3.12 2021-07-14 16:28:43 +02:00
Ricki Hirner
dbd0dd0a9b Lint cleanup 2021-07-12 13:40:13 +02:00
Ricki Hirner
267e73723c Version bump to 3.3.12-beta3 2021-07-12 12:49:16 +02:00
Ricki Hirner
42be478ab6 Update test dependencies 2021-07-12 12:42:56 +02:00
Ricki Hirner
ed76789417 AccountsActivity: use model for data/view separation 2021-07-12 12:36:08 +02:00
Ricki Hirner
ee48c41259 Fetch translations from Transifex 2021-07-08 14:38:49 +02:00
Ricki Hirner
137a916b20 Version bump to 3.3.12-beta2 2021-07-08 14:31:48 +02:00
Ricki Hirner
a7abb34fbe Update dependencies 2021-07-08 14:18:55 +02:00
Ricki Hirner
47d317bce9 Create address book/calendar: set error on TextInputLayout instead of TextInputEditText, optimize layout 2021-07-08 11:16:58 +02:00
Ricki Hirner
b918b49ec2 Provide auto-completion for some common base URLs 2021-07-06 20:51:22 +02:00
Ricki Hirner
235b9b1ac6 Change remaining Spinners to AutoCompleteTextInput fields; update dependencies 2021-07-06 20:51:22 +02:00
Ricki Hirner
663957b7dc Rewrite HomeSetAdapter so that it uses a Material-style text field instead of an old-style Spinner that doesn't work with dark mode anymore 2021-07-02 00:32:15 +02:00
Ricki Hirner
23235991d2 Log last sync time for each collection 2021-07-02 00:32:15 +02:00
Ricki Hirner
b94935db50 SyncManager: remove unused code 2021-06-29 19:30:32 +02:00
Ricki Hirner
6cd2af292d Fetch translations from Transifex 2021-06-29 19:11:37 +02:00
Ricki Hirner
51ee39c4f0 Version bump to 3.3.12-beta1 2021-06-29 19:11:37 +02:00
Ricki Hirner
ed722d574e SyncManager: don't create worker threads when not necessary; shut Executor down after using it 2021-06-29 19:11:37 +02:00
Ricki Hirner
2d388eb3ff Debug info activity: create separate card for archive, provide UI feedback for archive generation 2021-06-25 18:08:47 +02:00
Ricki Hirner
7b4ccb3fd4 Set UI theme directly in App.onCreate() and not in an asynchronous thread to avoid rare occurrences of IllegalStateException 2021-06-25 15:38:58 +02:00
Ricki Hirner
d67f57dd13 Update dependencies 2021-06-25 15:28:45 +02:00
Ricki Hirner
36a8ccf740 Debug info .zip: change MIME type from application/zip to */* 2021-06-21 11:56:02 +02:00
Ricki Hirner
616833c9dc Remove deprecated kotlin-android-extensions in favor of view bindings 2021-06-06 20:46:57 +02:00
Ricki Hirner
520f96f017 Update dependencies 2021-06-06 20:41:52 +02:00
Ricki Hirner
0b4fdaffe2 Debug info: use max compression for .zip 2021-06-06 12:12:37 +02:00
Ricki Hirner
0da6b5b032 Changelog for F-Droid 2021-05-26 12:34:37 +02:00
Ricki Hirner
ad4caecf7d Version bump to 3.3.11 2021-05-26 12:13:39 +02:00
Ricki Hirner
d87789690b Catch DB errors while renaming account 2021-05-23 14:32:03 +02:00
Ricki Hirner
5f5da4681e Fetch translations from Transifex 2021-05-23 14:04:49 +02:00
Ricki Hirner
b87c881f12 Version bump to 3.3.11-rc1 2021-05-23 14:03:42 +02:00
Ricki Hirner
bd45588865 Debug info: show app standby bucket; update AndroidX dependencies; minor night mode updates 2021-05-23 14:03:19 +02:00
Ricki Hirner
be99cb0a18 Debug info: show app standby bucket; update AndroidX dependencies 2021-05-23 14:03:00 +02:00
Ricki Hirner
3545cf30cf Remove comments which are not understood by Transifex 2021-05-11 15:04:29 +02:00
Ricki Hirner
6178f39639 Minor amendments for dark theme 2021-05-08 21:31:43 +02:00
Ricki Hirner
c8eae2c90e Version bump to 3.3.11-beta2 2021-05-07 23:06:06 +02:00
Ricki Hirner
78fb37516f Update ical4j 2021-05-07 23:05:36 +02:00
Ricki Hirner
f74c613b53 Dark theme, animation for WelcomeFragment 2021-05-07 22:54:42 +02:00
Ricki Hirner
9ce481b37a Don't use jCenter anymore 2021-05-04 21:02:19 +02:00
Ricki Hirner
d6dc42eb91 Update gradle plugin and version 2021-05-04 21:02:19 +02:00
Ricki Hirner
20034a8f32 Fix Espresso tests 2021-05-04 21:02:19 +02:00
Ricki Hirner
8b43c6f280 Merge branch 'dev-3.x_EspressoExperimental-ose' into 'dev-3.x-ose'
Dev 3.x espresso experimental ose

See merge request bitfireAT/davx5-ose!59
2021-05-04 17:20:44 +00:00
Patrick Lang
a5c51bc05f Dev 3.x espresso experimental ose 2021-05-04 17:20:44 +00:00
Ricki Hirner
6f398701cc Update dependencies 2021-05-04 15:56:10 +02:00
Ricki Hirner
fc1b5d6a33 Merge branch 'dev-3.x_RTLSupportEnabled-ose' into dev-3.x-ose 2021-05-04 12:02:01 +02:00
Ricki Hirner
b44ed202a3 ical4android: assume 1 day/1 hour for Android events without dtEnd and duration (should not occur, but occurs) 2021-04-21 15:14:34 +02:00
Ricki Hirner
90b61d541e Add Vietnamese translations (thanks!); fetch translations from Transifex 2021-04-19 18:54:22 +02:00
Ricki Hirner
42832f22c9 Version bump to 3.3.11-beta1 2021-04-19 18:52:52 +02:00
Ricki Hirner
dffe5d176b Android 11: fix problems when viewing URLs in some default browsers; vcard4android: don't convert TYPEs into explicit custom labels 2021-04-19 18:52:20 +02:00
Ricki Hirner
7122e344b7 F-Droid changelog for 3.3.10 2021-04-15 17:43:53 +02:00
Ricki Hirner
26900c5d4f Version bump to 3.3.10 2021-04-15 17:37:08 +02:00
Patrick Lang
f7c0ef231b Update .gitlab-ci.yml file 2021-04-13 18:06:48 +00:00
Patrick Lang
5c430664a9 Update .gitlab-ci.yml file, added CURL: - curl -XPOST -d 'email=gitlab%40bitfire.at&password=$DAVTEST_TOKEN&action=Request+access' 'https://davtest.dev001.net/access/' 2021-04-13 17:43:05 +00:00
Ricki Hirner
d9d593a4d6 Fetch translations from Transifex 2021-04-12 23:03:04 +02:00
Ricki Hirner
6790d95b21 Update build tools, ical4android, cert4android 2021-04-12 22:59:55 +02:00
Ricki Hirner
db2c207669 Version bump to 3.3.10-rc1 2021-04-09 20:54:26 +02:00
Ricki Hirner
71d6402ff2 Update dependencies (including ical4j to 3.0.24) 2021-04-09 20:54:06 +02:00
Ricki Hirner
e19ae992ea Client certificates: fix occasional HTTP 400 errors
* don't catch and ignore exceptions when adding client certificates for authentication
* remove useless KeyStore usage
2021-04-06 13:37:42 +02:00
Ricki Hirner
ef63165783 Update dependencies 2021-03-20 13:03:31 +01:00
Ricki Hirner
9048af4af5 Update ical4android 2021-03-20 12:38:58 +01:00
Ricki Hirner
512754d014 Typo 2021-03-12 12:12:52 +01:00
Ricki Hirner
8fdf57ea28 Version bump to 3.3.10-beta2 2021-03-08 19:20:38 +01:00
Ricki Hirner
a1506e1514 Update Kotlin, ical4android 2021-03-08 19:20:13 +01:00
Ricki Hirner
3049f7304d Update AboutLibraries 2021-03-08 18:50:54 +01:00
Ricki Hirner
b1d230353e debug info: wording; update JUnit 2021-03-08 18:45:12 +01:00
Ricki Hirner
f9a6107f38 Debug info: allow sharing only after debug info is generated 2021-03-02 12:31:19 +01:00
Ricki Hirner
266490bf14 Update libraries 2021-03-02 11:53:01 +01:00
Ricki Hirner
c037b7e152 Version bump to 3.3.10-beta1 2021-02-12 17:59:28 +01:00
Ricki Hirner
83f257f07a Events: support URL as extended property; rewrite deprecated unknown properties to current format 2021-02-12 17:47:11 +01:00
Ricki Hirner
1d35f80afb Fetch translations from Transifex 2021-02-09 11:02:45 +01:00
Ricki Hirner
46b78c043b Version bump to 3.3.9 2021-02-09 11:00:27 +01:00
Ricki Hirner
0687b8599e Version bump to 3.3.9-beta4 2021-02-07 13:12:51 +01:00
Ricki Hirner
77bd44044e Fix foreground notification crash 2021-02-07 12:56:11 +01:00
Ricki Hirner
e95366adcc Fix translations 2021-02-07 12:01:26 +01:00
Ricki Hirner
a1d689eb1b Fetch translations from Transifex 2021-02-07 12:01:26 +01:00
Ricki Hirner
2d5b879b34 Update kotlin and cert4android 2021-02-07 12:01:26 +01:00
Ricki Hirner
7667ecb437 Version bump to 3.3.9-beta3 2021-02-01 14:58:25 +01:00
Ricki Hirner
6942ad9867 Debug info activity: remove string 2021-02-01 14:40:09 +01:00
Ricki Hirner
8aba0c80bc New option: keep in foreground (may help when the device prevents automatic synchronization) 2021-02-01 14:40:09 +01:00
Ricki Hirner
652b0ba92c Version bump to 3.3.9-beta2 2021-01-31 13:45:57 +01:00
Ricki Hirner
a92593ea77 Update okhttp 2021-01-31 13:45:41 +01:00
Ricki Hirner
3fb4e305ad Debug info: show number of events, contacts, tasks for each account (to know whether sync is working) 2021-01-31 13:45:40 +01:00
Ricki Hirner
e96f8a627e Version bump to 3.3.9-beta1 2021-01-16 14:18:11 +01:00
Ricki Hirner
eebd21a02e AccountActivity: don't show "Create addressbook/calendar" when there's no bind privilege on any homeset 2021-01-16 12:53:32 +01:00
Ricki Hirner
8323ea1872 AccountActivity: finish when account has been removed externally 2021-01-16 12:53:32 +01:00
Ricki Hirner
2f1ee9994d *DAV service detection: save all information of homesets again 2021-01-16 12:53:32 +01:00
Ricki Hirner
e157322a1f Correctly show dav4jvm in About/Libraries 2021-01-16 12:53:32 +01:00
Ricki Hirner
40a35ac7a0 Version bump to 3.3.8 2021-01-11 20:10:34 +01:00
Ricki Hirner
d0385ea8ce Fetch translations from Transifex 2021-01-11 20:08:37 +01:00
Ricki Hirner
4a89bb762e Catch some exceptions 2021-01-11 20:05:18 +01:00
Ricki Hirner
51c96a50c0 Update vcard4android 2021-01-06 10:25:51 +01:00
Ricki Hirner
16464ed025 Fetch translations from Transifex 2021-01-02 15:27:43 +01:00
Ricki Hirner
5567e79d2d Update libraries and dependencies 2021-01-02 15:11:45 +01:00
Ricki Hirner
ececb08bb6 Debug info: include interface name and DNS servers (because DNS-related problems are reported often) 2020-12-23 16:49:21 +01:00
Ricki Hirner
07fccd86d1 Version bump to 3.3.8-beta1 2020-12-14 19:12:18 +01:00
Ricki Hirner
4e870123ff Merge branch 'dev-3.x_DontShowUnspportedCollections-ose' into dev-3.x-ose 2020-12-14 19:06:31 +01:00
Patrick
e2e852c2e0 Exclude unsupported calendar collections
Excludes calendar collections that have both VEVENT and VTODO not supported from being displayed in the collections overview.
2020-12-14 19:04:31 +01:00
Ricki Hirner
46ee17f04b Make default sync interval a setting 2020-12-14 18:52:06 +01:00
Ricki Hirner
21fffe0b65 Update dependencies 2020-12-14 18:51:52 +01:00
Ricki Hirner
35a6ddb89c Bump version code 2020-11-28 14:32:48 +01:00
Ricki Hirner
be48945c03 Support resizeable activites (multi-window mode) 2020-11-28 14:26:38 +01:00
Ricki Hirner
1a7b1f9420 Update ical4android and version code 2020-11-28 13:38:25 +01:00
Ricki Hirner
9e1dc44048 Version bump to 3.3.7 2020-11-28 08:58:47 +01:00
Ricki Hirner
30348b40a4 Fetch translations from Transifex 2020-11-27 21:47:02 +01:00
Ricki Hirner
7af2b8bd00 Version bump to 3.3.7-beta3 2020-11-27 21:46:16 +01:00
Ricki Hirner
90c503fc16 add "show only personal" as setting 2020-11-27 21:45:28 +01:00
Ricki Hirner
6411dfef3a Update ical4android and vcard4android; add HTTP/2 ping every 45 secs to avoid cancellation 2020-11-26 11:39:18 +01:00
Ricki Hirner
6b84350df5 Minor rewriting 2020-11-24 12:14:34 +01:00
Ricki Hirner
f1d06a305f Fetch translations from Transifex 2020-11-23 11:48:42 +01:00
Ricki Hirner
38eb9693d2 Version bump to 3.3.7-beta2 2020-11-23 11:39:00 +01:00
Ricki Hirner
c7c547e391 Update ical4android, vcard4android 2020-11-23 00:31:34 +01:00
Ricki Hirner
0226414458 Save sync intervals and check/repair them at app start and reboot 2020-11-19 17:51:24 +01:00
Ricki Hirner
7a95bfdb94 Update AboutLibraries 2020-11-17 23:36:12 +01:00
Ricki Hirner
491763025b Merge branch 'dev-3.x_NoCertificatesFoundToast-ose' into dev-3.x-ose 2020-11-16 09:22:11 +01:00
Patrick
2d6b9eef4d Shows a toast when no certificate was found
Currently the certificate selection would popup und disappear immedeately. This quick fix would show a Toast for User Feedback
2020-11-16 09:20:30 +01:00
Ricki Hirner
0a06d0ea11 Account settings: always allow client certificate selection 2020-11-16 09:05:14 +01:00
Ricki Hirner
737af880bd Add migration for AppDatabase v8 2020-11-15 22:41:54 +01:00
Ricki Hirner
19474a1e17 Version bump to 3.3.7-beta1 2020-11-15 21:38:04 +01:00
Ricki Hirner
326267afce DAV:owner: resolve relative paths, only show in UI when it's set 2020-11-15 21:32:28 +01:00
Ricki Hirner
8ff0a192b8 Update submodules 2020-11-15 21:13:21 +01:00
Ricki Hirner
e9a62eddf5 DAV:owner small rewrite 2020-11-15 21:11:39 +01:00
Patrick Lang
ced1ed9abf Added Owner
- Extend Collection to store owner
- Show Owner in collection_properties.xml
2020-11-15 20:04:32 +00:00
Ricki Hirner
a1898888cb Fix tests 2020-11-15 20:47:31 +01:00
Ricki Hirner
7e5a7dff20 Add an account setting for "Show only personal collections" 2020-11-15 20:35:46 +01:00
Ricki Hirner
5c44332e69 Add account view option: show only personal collections 2020-11-15 20:35:46 +01:00
Ricki Hirner
4fb6e39148 Associate each collection to its homeset 2020-11-15 20:35:46 +01:00
Ricki Hirner
2c28ffad2e Update dependencies 2020-11-14 13:09:48 +01:00
Patrick
674455cc8c Merge branch 'dev-3.x-ose' into dev-3.x_RTLSupportEnabled-ose 2020-11-11 19:36:28 +01:00
Ricki Hirner
540d1ddb74 Merge branch 'infomaniak/android-infomaniak-sync-fix-string-localization' into dev-3.x-ose 2020-11-10 22:04:09 +01:00
Joris Bodin
fb4f870742 Extract string for remove auto reset permission 2020-11-10 22:03:46 +01:00
Ricki Hirner
1a8fca415d Update ez-vcard 2020-11-10 13:34:07 +01:00
Ricki Hirner
b69ff2380a Wifi permission activity: add "Allow all the time" string for Android 10 2020-11-07 12:31:12 +01:00
Ricki Hirner
260c5f928d Version bump to 3.3.6 2020-11-07 12:31:12 +01:00
Patrick
d199560add Merge branch 'dev-3.x-ose' into dev-3.x_RTLSupportEnabled-ose 2020-11-06 21:03:26 +01:00
Ricki Hirner
775a8abc60 cert4android: fetch translations from Transifex 2020-11-05 12:10:14 +01:00
Ricki Hirner
74f2bf69d6 Fetch translations from Transifex 2020-11-05 12:09:19 +01:00
Ricki Hirner
c583d92205 Version bump to 3.3.6-beta2 2020-11-05 12:06:36 +01:00
Ricki Hirner
64cb191c6d Try to work around problems with package visibility: use package instead of provider in visibility declaration 2020-11-05 12:06:22 +01:00
Ricki Hirner
b84ef186d8 Merge branch 'mbiebl/davx5-ose-share-icon-dark' into dev-3.x-ose 2020-11-03 13:14:48 +01:00
Ricki Hirner
bf08231bbd Don't sync Webcal-only calendars/tasks lists over CalDAV (may cause duplicate calendars)
(If a calendar is both CalDAV- and Webcal-capable, i.e. resourcetype is collection
and subscribed, it will be shown as CalDAV calendar.)
2020-11-03 12:58:48 +01:00
Patrick
907c1cb1ba Fixed layout gravity in login_credentials_fragment 2020-10-31 14:12:22 +01:00
Patrick
203fa7b1cd RTL Support enabled
+ Checked all Screens
+ checked drawables and mirrored if applicable
+ checked screens without translations to still fulfill RTL layout
2020-10-31 13:03:33 +01:00
Ricki Hirner
1973fc4f29 Add login layout ID for pre-launch tests; version bump to 3.3.6-beta1 2020-10-30 18:23:32 +01:00
Ricki Hirner
e6cc326166 Check whether WiFi permissions are declared 2020-10-30 12:52:42 +01:00
Ricki Hirner
1f47798cf7 Minor refactoring: About, styles 2020-10-29 20:19:23 +01:00
Ricki Hirner
1ed91ff11a Use Western digits where Western digits are required; show locale in debug info 2020-10-26 18:28:32 +01:00
Ricki Hirner
0782af6e91 cert4android: use MD5 fingerprint instead of CN as key to store custom certs 2020-10-25 20:26:08 +01:00
Ricki Hirner
99f59286c7 Really disable calendar sync for an account when there is no CalDAV service; update ez-vcard 2020-10-25 17:56:55 +01:00
Ricki Hirner
ecdf2607f9 Update ez-vcard 2020-10-25 15:53:40 +01:00
Ricki Hirner
c4440bfc4f Always enable verbose logs in debug builds; show SyncResult when a sync is complete 2020-10-25 14:19:05 +01:00
Ricki Hirner
96aa2fe3ce Don't ask for active location services on Android <9 / location permissions on Android 8.0 2020-10-25 10:28:29 +01:00
Ricki Hirner
ca9044cf76 Fetch translations from Transifex 2020-10-24 14:02:00 +02:00
Ricki Hirner
88bbb3c8bf Update ical4android; version bump to 3.3.5 2020-10-24 13:52:42 +02:00
Ricki Hirner
b920cdb6f8 Add ICSx5 to visible packages so that it can be detected under Android 11 2020-10-23 13:11:45 +02:00
Ricki Hirner
baedab5228 Account settings: make password a password field 2020-10-23 00:33:52 +02:00
Ricki Hirner
8e0d85615f When an account is created, open it immediately after setup 2020-10-22 18:21:50 +02:00
Ricki Hirner
dc1d5b6016 Don't require location permissions for SSID access on Android <8 2020-10-21 13:21:22 +02:00
Ricki Hirner
b75064ee3c Version bump to 3.3.4.1 2020-10-20 12:20:04 +02:00
Ricki Hirner
f39a0c2459 Fetch translations from Transifex 2020-10-20 12:19:25 +02:00
Ricki Hirner
93dd713c41 Sync algorithm: when uploading a locally modified resource results in 404/410, try as fresh upload again 2020-10-20 12:10:24 +02:00
Ricki Hirner
817e2e834d Fetch translations from Transifex 2020-10-19 23:17:03 +02:00
Ricki Hirner
5e071c7a61 Fix libraries; version bump to 3.3.4 2020-10-19 23:16:11 +02:00
Ricki Hirner
26a7c72ad7 Update ical4j to 3.0.20 2020-10-19 20:38:43 +02:00
Michael Biebl
9323464dd1 Use dark variant of share icon in Debug activity
We typically provide a dark variant for icons that are used on a light
background. The current share icon sticks out a bit, so add a dark
variant of that icon and use that in the Debug activity.
2020-10-19 16:14:10 +02:00
Ricki Hirner
244dd7e3e7 Version bump to 3.3.4-beta1 2020-10-18 21:20:47 +02:00
Ricki Hirner
f365570952 Advanced login: small fixes 2020-10-18 12:32:56 +02:00
Patrick Lang
4f1d546174 Login with advanced options
Adds a new radiobutton for login with advanced options
2020-10-17 18:58:08 +00:00
Michael Biebl
998da1bebf Typo fixes 2020-10-17 11:45:32 +02:00
Ricki Hirner
3e2bb92a15 F-Droid Changelog 2020-10-16 14:05:15 +02:00
Ricki Hirner
b77586e31c Fetch translations from Transifex 2020-10-16 13:06:27 +02:00
Ricki Hirner
204e0444f9 Version bump to 3.3.3 2020-10-16 13:02:38 +02:00
Ricki Hirner
ebed7e63c2 Nextcloud Login Flow v2: better error handling 2020-10-16 13:02:20 +02:00
Ricki Hirner
0d9c78ee07 Debug info: add Share action hint 2020-10-16 12:40:48 +02:00
Ricki Hirner
6666b28f31 Version bump to 3.3.3-beta4 2020-10-15 18:18:28 +02:00
Ricki Hirner
acbd93ce7e Update gradle plugin to 4.1; update dependencies 2020-10-15 18:08:39 +02:00
Ricki Hirner
61904434ff Fetch translations from Transifex 2020-10-14 13:26:49 +02:00
Ricki Hirner
33f5cf8099 Version bump to 3.3.3-beta3 2020-10-14 13:25:36 +02:00
Ricki Hirner
d52e94ebe8 Fix bug where OpenTasks permissions where hidden in UI when tasks.org was not installed 2020-10-13 20:34:09 +02:00
Ricki Hirner
72faa6a456 Update dependencies 2020-10-13 20:33:36 +02:00
Ricki Hirner
3204000411 Nextcloud: use Login Flow v2 for better browsing experience and to support things like Webauthn 2020-10-05 15:45:32 +02:00
Ricki Hirner
63572ad62a Debug info: show all permissions 2020-10-04 10:45:49 +02:00
Ricki Hirner
394e319959 Version bump to 3.3.3-beta2 2020-10-03 14:22:36 +02:00
Ricki Hirner
bd154a6315 Fetch translations from Transifex 2020-10-03 14:21:23 +02:00
Ricki Hirner
35b92f4469 Background location: use mandatory wording for explanation [https://support.google.com/googleplay/android-developer/answer/9799150?hl=en#prominent_disclosure] 2020-10-03 14:11:43 +02:00
Ricki Hirner
eaf5b37c01 Version bump to 3.3.3-beta1 2020-10-02 13:12:24 +02:00
Ricki Hirner
2656e7e58d Fetch translations from Transifex 2020-10-02 13:04:41 +02:00
Ricki Hirner
7e98563038 Improve logging notification/debug info; fix account settings UI WiFi permission info 2020-10-02 13:03:33 +02:00
Ricki Hirner
4a83df50b6 Error "Expected 207, got 200": show HTTP request/body in debug info 2020-10-01 12:23:12 +02:00
Ricki Hirner
cea038f29b Small compatibility fix/lint 2020-10-01 11:35:47 +02:00
Ricki Hirner
0bb1aa0243 Android 11: declare package visibility; adapt debug info 2020-10-01 11:05:17 +02:00
Ricki Hirner
de7ca2cf16 Refactor navigation from/to account settings activity 2020-10-01 11:05:17 +02:00
Ricki Hirner
f057b41c0f Android 11: handle new background location permissions for WiFi SSID restriction 2020-10-01 11:05:17 +02:00
Ricki Hirner
ab5f834ded Android 11: increase target SDK level; PermissionsFragment: ask to keep permissions on Android >= 11 2020-10-01 11:05:15 +02:00
Ricki Hirner
31c9f0f745 F-droid release notes 2020-09-29 15:57:48 +02:00
Ricki Hirner
d9f8185604 Version bump to 3.3.2 2020-09-29 15:23:53 +02:00
Ricki Hirner
da38fab28d Version bump to 3.3.2-beta5 2020-09-27 16:55:42 +02:00
Ricki Hirner
511ae6ad4e Fetch translations from Transifex 2020-09-27 16:55:12 +02:00
Ricki Hirner
1883bc8f64 Sync intervals: remove endless loop, add tests 2020-09-27 16:48:30 +02:00
Ricki Hirner
def9222148 Cache task provider in the UI 2020-09-25 12:46:41 +02:00
Ricki Hirner
9c2b81017f Collections fragment: use current task provider's status only 2020-09-25 12:46:41 +02:00
Ricki Hirner
8a8ae586c1 Version bump to 3.3.2-beta4 2020-09-21 11:14:22 +02:00
Ricki Hirner
517e1daea9 Fix tasks sync interval setting for tasks.org 2020-09-20 22:30:01 +02:00
Ricki Hirner
559665a481 Bump version to 3.3.2-beta3 2020-09-20 14:23:02 +02:00
Ricki Hirner
65f5f3cf62 Fetch translations from Transifex 2020-09-20 14:22:29 +02:00
Ricki Hirner
92bf8d55a1 Fix permissions UI 2020-09-20 14:21:05 +02:00
Ricki Hirner
093671a43d Dependencies; lint 2020-09-19 14:50:39 +02:00
Ricki Hirner
36263e0044 Fetch translations from Transifex 2020-09-18 16:54:19 +02:00
Ricki Hirner
1ea156750c Update cert4android/vcard4android, bump version to 3.3.2-beta2 2020-09-18 16:52:39 +02:00
Ricki Hirner
799eb05097 Small TasksWatcher fix; update ical4android and build tools 2020-09-17 19:38:43 +02:00
Ricki Hirner
1a92067262 Version bump to 3.3.2-beta1 2020-09-15 21:59:37 +02:00
Ricki Hirner
a9d652684e tasks.org support 2020-09-15 21:59:18 +02:00
Ricki Hirner
1cd17a88f7 Update strings 2020-09-14 10:15:40 +02:00
Ricki Hirner
5a9dc79595 Move translation pt-rBR to pt 2020-09-13 18:09:26 +02:00
Ricki Hirner
62a4420d73 improve HTTP requests in debug info; cert4android update 2020-09-09 21:49:45 +02:00
Ricki Hirner
ff8de74f21 Add F-Droid Changelog 2020-09-09 12:54:21 +02:00
Ricki Hirner
ba973a7320 Fetch translations from Transifex 2020-09-09 12:06:12 +02:00
Ricki Hirner
5bfc343629 Version bump to 3.3.1; minor dependency updates 2020-09-09 12:00:02 +02:00
Ricki Hirner
d6df81586b Fetch translations from Transifex 2020-08-31 12:43:17 +02:00
Ricki Hirner
c502834530 Version bump to 3.3.1-beta1 2020-08-31 12:41:53 +02:00
Ricki Hirner
638f9f9264 Adaptation for Google Contacts in Android 11: export address book authenticator so that Google Contacts shows the address book accounts 2020-08-31 12:41:34 +02:00
Ricki Hirner
ed38d38f20 Scripts etc. 2020-08-31 12:41:33 +02:00
Ricki Hirner
838855b9e8 Add F-Droid changelog for 3.3 2020-08-23 18:02:32 +02:00
Ricki Hirner
99502d827d Fetch translations from Transifex 2020-08-22 13:21:15 +02:00
Ricki Hirner
0ee9ed4999 Version bump to 3.3 2020-08-22 13:17:36 +02:00
Ricki Hirner
b2a6bddfa5 Update dependencies (including ical4android) 2020-08-22 13:17:04 +02:00
Ricki Hirner
6898e05b7f Version bump to 3.3-beta5 2020-08-15 13:36:17 +02:00
Ricki Hirner
83eca1ea3d Update app settings dynamically when they are changed 2020-08-15 12:37:58 +02:00
Ricki Hirner
4e42aa1375 Use plain-text debug info instead of HTML, again
* use UTF-8
* use plain-text tables
2020-08-15 12:19:58 +02:00
Ricki Hirner
949049f414 Fetch translations from Transifex 2020-08-12 12:12:11 +02:00
Ricki Hirner
5346095d66 Version bump to 3.3-beta4 2020-08-12 12:11:23 +02:00
Ricki Hirner
7358461ecc Add settings to debug info 2020-08-12 12:11:09 +02:00
Ricki Hirner
22c399e6fd Fixes for Android <25 2020-08-10 15:32:30 +02:00
Ricki Hirner
6b2828646e Version bump to 3.3-beta3 2020-08-10 15:09:36 +02:00
Ricki Hirner
6ce830442d Rewrite DebugInfoActivity 2020-08-10 15:09:15 +02:00
Ricki Hirner
0129f4bcf8 Fix Sync all shortcut 2020-08-10 15:09:14 +02:00
Ricki Hirner
d4942cbf14 Some lint 2020-08-07 12:23:50 +02:00
Ricki Hirner
f18a8cb9ab Update AppIntro 2020-08-07 11:44:32 +02:00
Ricki Hirner
f4194a7f95 Update dependencies 2020-08-07 11:44:21 +02:00
Ricki Hirner
a79ec19c6a Provide "Sync all accounts" option
* show sync status in accounts overview
* add "sync all accounts" in accounts overview
* add "sync all accounts" shortcut
2020-08-02 17:22:12 +02:00
Ricki Hirner
a14169d19d Bump version code 2020-08-02 17:14:36 +02:00
Ricki Hirner
28dc549744 Fix isSyncable problems
- set isSyncable(AddressBooks)=0 at account creation when there is no CardDAV service
- OpenTasksWatcher: enumerate real accounts and not services from DB -> isSyncable(tasks) will be set to 0 correctly for accounts without CalDAV service
- SyncAdapter base class: set autoInitialize to true, although it should not be needed regularly
2020-08-02 17:14:24 +02:00
Ricki Hirner
3069d79e8b Version bump to 3.2.1.4 2020-08-01 00:05:15 +02:00
Ricki Hirner
2e9f4c4a95 Update ical4android, vcard4android 2020-08-01 00:04:53 +02:00
Ricki Hirner
a833d002df Don't grey out WiFi SSID setting when data saver is active; update dependencies 2020-08-01 00:04:53 +02:00
Ricki Hirner
87a52cceef Sync interval settings: wait with UI update until settings have arrived in sync framework 2020-08-01 00:04:53 +02:00
Ricki Hirner
349c1beab5 Fetch translations from Transifex 2020-07-25 11:55:20 +02:00
Ricki Hirner
8835220116 Update vcard4android, version bump to 3.2.1.3 2020-07-25 11:50:04 +02:00
Ricki Hirner
6982304ea3 Nextcloud login flow: decode parameters in nc://login URL 2020-07-24 18:39:20 +02:00
Ricki Hirner
4aee529457 Rename account: handle exceptions (for instance, when the desired name already exists) 2020-07-22 13:16:43 +02:00
Ricki Hirner
6e84ea52c8 Fetch translations from Transifex 2020-07-22 11:13:05 +02:00
Ricki Hirner
ab3122e731 Version bump to 3.2.1.2 2020-07-22 11:12:18 +02:00
Ricki Hirner
3325e6a9bc Update dependencies 2020-07-21 15:54:43 +02:00
Ricki Hirner
b120a797f2 Sort accounts in AccountsActivity by name 2020-07-20 19:17:12 +02:00
Ricki Hirner
eca4ac177d Version bump to 3.2.1.2-beta1 2020-07-15 13:10:19 +02:00
Ricki Hirner
c5960b6b40 Regenerate UID if it was dropped from an existing resource 2020-07-15 13:09:43 +02:00
Ricki Hirner
0f44b63262 Version bump to 3.2.1.1 2020-07-11 22:26:48 +02:00
Ricki Hirner
01ae43bdcc Fix crash in ical4android, update okhttp 2020-07-11 22:26:28 +02:00
Ricki Hirner
c6d4a07b05 Fix SyncManager tests on Android 5 2020-07-10 18:15:02 +02:00
Ricki Hirner
8104ed4aed SyncManager tests 2020-07-10 17:21:49 +02:00
Ricki Hirner
4f0236e14b Fetch translations from Transifex 2020-07-09 14:59:56 +02:00
Ricki Hirner
86c49f2afa Version bump to 3.2.1 2020-07-09 14:59:04 +02:00
Ricki Hirner
ab489dbb44 Sync fix 2020-07-09 14:58:48 +02:00
Ricki Hirner
e0d0ef7370 Version bump to 3.2 2020-07-07 17:08:07 +02:00
Ricki Hirner
047a115171 Minor settings provider change 2020-07-07 16:50:17 +02:00
Ricki Hirner
fd72d4611f Fetch translations from Transifex 2020-07-07 16:49:36 +02:00
Ricki Hirner
519dda024b Update ical4android 2020-07-07 16:20:36 +02:00
Ricki Hirner
c746aa4ddf Lint 2020-07-06 12:26:18 +02:00
Ricki Hirner
ec6b2dd16a Version bump to 3.2-beta3 2020-07-06 11:19:56 +02:00
Ricki Hirner
1e57b83a66 Sync algorithm: small refactoring 2020-07-06 11:14:12 +02:00
Ricki Hirner
32b0c8355b Merge branch 'verify-user-data' into 'dev-3.x-ose'
When creating an addressbook account, verify the stored user data

See merge request bitfireAT/davx5-ose!33
2020-07-05 13:34:16 +00:00
Ricki Hirner
8e2bee7d36 Improve debug info: software information 2020-07-05 15:30:47 +02:00
Ricki Hirner
3c202d35d5 Set address book accounts isSyncable=1 at creation (and not only after first sync) 2020-07-05 15:30:47 +02:00
Alex Baker
d82341f310 Clear url, username, and name errors 2020-07-05 15:30:47 +02:00
Michael Biebl
77c00414c1 Intro: Use consistent padding among screens
The battery optimization screen used some additional padding which made
it look out of place when swiping through the intro screens.
Instead of applying app:cardUseCompatPadding="true" to the other
screens, we remove it from intro_battery_optimizations.xml.

While at it, fix the indentation of the TextView element.
2020-07-05 15:30:47 +02:00
Ricki Hirner
4b26ab1f34 rename useLocal and useRemote for better readability 2020-07-05 15:30:47 +02:00
Ricki Hirner
9582e08d0e Sync algorithm: use resource name (and not ETag) to determine whether a dirty local record is new or modified 2020-07-05 15:30:47 +02:00
Ricki Hirner
0cdb2d1ff3 Fetch translations from Transifex 2020-07-03 16:49:54 +02:00
Ricki Hirner
31b75cffa3 Version bump to 3.2-beta2 2020-07-03 16:41:35 +02:00
Ricki Hirner
71111172cb Task improvements 2020-07-03 16:41:17 +02:00
Ricki Hirner
357e7cca24 Login: show "username/password wrong" when 401 is encountered during resource detection; update ical4android 2020-07-03 11:34:14 +02:00
Ricki Hirner
a652d5bccf Improve Schedule-Tag handling 2020-07-03 11:34:14 +02:00
Ricki Hirner
c794e1ffbb Resource detection: evaluate priority/weight of multiple SRV records 2020-07-03 11:34:14 +02:00
Ricki Hirner
9350818029 Events scheduling: support schedule-tag 2020-07-03 11:34:14 +02:00
Ricki Hirner
9f0db15bca Fetch translations from Transifex 2020-06-29 23:15:13 +02:00
Ricki Hirner
73e2d554fb Update dependencies; bump version to 3.1-beta2 2020-06-29 23:14:20 +02:00
Ricki Hirner
fd44aea3ce Minor group-scheduling improvements; ical4android: always generate DTEND instead of DURATION 2020-06-29 23:13:52 +02:00
Ricki Hirner
c0cf9194b7 Shorten PRODID; don't minify debug builds anymore; ical4android update 2020-06-22 16:25:03 +02:00
Ricki Hirner
ca3b308018 Show CalDAV/CardDAV account settings only when CalDAV/CardDAV is present; update ical4android 2020-06-19 13:46:54 +02:00
Ricki Hirner
c9ccbb73ac Update dependencies 2020-06-18 17:44:03 +02:00
Ricki Hirner
9ee65a229d Always set "Sync over WiFi only" when data saver is active 2020-06-16 17:10:23 +02:00
Michael Biebl
27e5504de8 When creating an addressbook account, verify the stored user data
Apparently even with newer Android versions we sometimes fail to store
the user data. If we fail to save the main account, synchronization will fail.
2020-06-14 23:41:15 +02:00
Ricki Hirner
d3e851cb58 Version bump to 3.1.1 2020-06-14 16:39:37 +02:00
Ricki Hirner
453200ab1e Version bump to 3.1.1-beta2 2020-06-12 13:39:47 +02:00
Ricki Hirner
c51677b001 ical4android update 2020-06-12 13:39:32 +02:00
Ricki Hirner
aeb0dd70f8 Use some Kotlin extensions 2020-06-11 23:22:46 +02:00
Ricki Hirner
47874bf7eb Fetch translations from Transifex 2020-06-11 18:32:10 +02:00
Ricki Hirner
e0f90bb311 Version bump to 3.1.1-beta1 2020-06-11 18:31:26 +02:00
Ricki Hirner
055fdeaf4c Use fragment-ktx viewModels(); begin removing I- prefix from interface names 2020-06-11 18:26:19 +02:00
Ricki Hirner
cc092daf72 R8: keep all ez-vcard properties/parameters in release builds 2020-06-11 14:38:49 +02:00
Ricki Hirner
9acbb49a27 Fix UI crash 2020-06-11 00:07:08 +02:00
Ricki Hirner
c536a8a50c Refactor settings management 2020-06-11 00:07:07 +02:00
Ricki Hirner
d3bed790ab Improve singletons and settings management 2020-06-11 00:03:43 +02:00
Ricki Hirner
2a24e05161 LocalAddressBook.findAll: don't return address book accounts without associated main account 2020-06-05 12:58:55 +02:00
Ricki Hirner
9e08e73fc6 AccountActivity: use constructor instead of initialize() to avoid non-initialized lateinit properties 2020-06-05 12:49:23 +02:00
Ricki Hirner
80df1eecaa Add F-Droid changelog 2020-05-31 21:05:40 +02:00
Ricki Hirner
4e062bc621 Version bump to 3.1 2020-05-31 20:52:14 +02:00
Ricki Hirner
02a1899109 Fetch translations from Transifex 2020-05-31 20:51:36 +02:00
Ricki Hirner
59180cda06 Version bump to 3.1-rc4 2020-05-31 17:36:48 +02:00
Ricki Hirner
74cb4963e4 Update libraries; use Kotlin for JDK7 2020-05-31 16:32:48 +02:00
Ricki Hirner
0e4e655d51 Link PermissionsActivity from app settings 2020-05-31 16:32:48 +02:00
Ricki Hirner
76020736bb Don't use R8 shrinking for debug builds/tests 2020-05-31 01:03:33 +02:00
Ricki Hirner
1e14bbfe42 Version bump to 3.1-rc3 2020-05-30 23:53:42 +02:00
Ricki Hirner
6f5363ef7a Workaround: Android loses initial account data sometimes 2020-05-30 23:53:42 +02:00
Ricki Hirner
cd85c4b05a Completely remove customCerts build flag; shrink resources for release builds 2020-05-30 23:53:42 +02:00
Ricki Hirner
4bef2420df Fix account name selection 2020-05-30 20:29:04 +02:00
Ricki Hirner
9f052f6231 Documentation: link to JDK8 2020-05-30 14:19:59 +02:00
Ricki Hirner
877d06abba Version bump to 3.1-rc2 2020-05-30 14:19:58 +02:00
Ricki Hirner
822720e597 Enable R8 desugaring also for debug builds 2020-05-30 14:19:02 +02:00
Ricki Hirner
24c583b784 Fetch translations from Transifex 2020-05-29 00:27:08 +02:00
Ricki Hirner
c3f6791baa Version bump to 3.1-rc1 2020-05-29 00:27:08 +02:00
Ricki Hirner
a09f1575c2 Update dependencies 2020-05-29 00:27:08 +02:00
Ricki Hirner
63898da6e3 Fix some TextInputLayouts 2020-05-27 16:20:47 +02:00
Ricki Hirner
558ced82da Show all detected email addresses as account name suggestion 2020-05-27 16:20:47 +02:00
Ricki Hirner
b59f6c8b7e Update logger tags 2020-05-27 16:20:47 +02:00
Ricki Hirner
85beb90b2f Fix exception in OpenTasksWatcher when account is not available (anymore) 2020-05-27 16:20:47 +02:00
Ricki Hirner
e732aa7ebd Version bump to 3.1-beta7 2020-05-27 16:20:47 +02:00
Ricki Hirner
eb38791d61 Drop customCerts build flag (alwase use cert4android and thus Conscrypt) 2020-05-27 16:20:47 +02:00
Ricki Hirner
447e7c3bdf Update dependencies 2020-05-27 16:20:47 +02:00
Ricki Hirner
a81eb8be51 Nextcloud login fragment: always use fragment view for Snackbar 2020-05-27 16:20:47 +02:00
Ricki Hirner
c0e2488ef8 Rename account: handle situation when account doesn't exist (anymore) 2020-05-27 16:20:47 +02:00
Ricki Hirner
34adafeac2 Account settings activity: handle situation when account is not available (anymore) 2020-05-27 16:20:47 +02:00
Ricki Hirner
cad2b15a81 Synchronize possibly simultaneous calls to ServiceLoader 2020-05-27 16:20:47 +02:00
Ricki Hirner
0670732cc8 Version bump to 3.1-beta6 2020-05-27 16:20:47 +02:00
Ricki Hirner
3089d42c9f Update dependencies (ical4android: minify VTIMEZONEs) 2020-05-27 16:20:47 +02:00
Ricki Hirner
b6bdf2dfaa Update dependencies, remove obsolete version checks 2020-05-27 16:20:47 +02:00
Ricki Hirner
5a657ba5e0 Update okhttp, use new version constant for User-Agent 2020-05-27 16:20:47 +02:00
Ricki Hirner
9055b34a1d Fetch translations from Transifex 2020-05-27 16:20:47 +02:00
Ricki Hirner
7bf1d5618a Version bump to 3.1-beta5 2020-05-27 16:20:47 +02:00
Ricki Hirner
f7d001ae1d ical4j: fix XParameter type cast problem with ATTENDEE EMAILs 2020-05-27 16:20:47 +02:00
Ricki Hirner
8cf8dadb8f Update copyright 2020-05-27 16:20:47 +02:00
Michael Biebl
d5d9f276a3 Clear all errors when a different login method is chosen
See https://forums.bitfire.at/topic/2275/error-message-not-updated-when-switching-to-different-login-method/
2020-05-27 16:20:47 +02:00
Ricki Hirner
a1509f8ce4 Update cert4android, add comment 2020-05-27 16:20:47 +02:00
Michael Biebl
af227737f5 Override Brotli meta data in About activity
This is a workaround until
https://github.com/mikepenz/AboutLibraries/issues/490 has been resolved.
2020-05-27 16:20:47 +02:00
Ricki Hirner
65ca1c37e8 Update dependencies 2020-05-27 16:20:47 +02:00
Ricki Hirner
1c2bfc582c Version bump to 3.1-beta4 2020-05-27 16:20:47 +02:00
Ricki Hirner
dbc7ca7cc3 Fetch translations from Transifex 2020-05-27 16:20:47 +02:00
Ricki Hirner
6ceca3793c Update dependencies 2020-05-27 16:20:46 +02:00
Ricki Hirner
2ff46e5219 Add app settings to debug info 2020-05-27 16:20:46 +02:00
Ricki Hirner
00cf40a694 Version bump to 3.1-beta3 2020-05-27 16:20:46 +02:00
Ricki Hirner
3445d0df54 Fetch translations from Transifex 2020-05-27 16:20:46 +02:00
Ricki Hirner
1b0720d798 Handle exceptions when event/task SEQUENCE is increased; update ical4android 2020-05-27 16:20:46 +02:00
Ricki Hirner
fa1ed29659 Show Donate in Navigation drawer only in ose 2020-05-27 16:20:46 +02:00
Ricki Hirner
f01b57bb66 Version bump to 3.1-beta2 2020-05-27 16:20:46 +02:00
Ricki Hirner
94fc3b7cdd Make sure Thread.getContextClassLoader is set while syncing (for ical4j)
* use dav4jvm version that doesn't depend on ServiceLoader anymore
2020-05-27 16:20:46 +02:00
Ricki Hirner
ffa40e5e13 Use coroutines instead of threads, when possible 2020-05-27 16:20:46 +02:00
Ricki Hirner
17846c2413 Permissions activity: add App settings button 2020-05-27 16:20:46 +02:00
Ricki Hirner
cc6194fdf6 Sync algorithm: use Kotlin coroutines instead of thread-pool executors 2020-05-27 16:20:46 +02:00
Ricki Hirner
ebcec6a5a4 Update dependencies; version bump to 3.1.0-beta1 2020-05-27 16:20:46 +02:00
Ricki Hirner
4977a1ab58 New permissions model 2020-05-27 16:20:46 +02:00
Ricki Hirner
7ef847ea35 Update gradle plugin 2020-05-27 16:20:46 +02:00
Ricki Hirner
5e0e26a5e5 Sync cancellation: show in logs, cancel whole thread group 2020-05-27 16:20:46 +02:00
Ricki Hirner
dcdc659882 Version bump to 3.0.1-beta1 2020-05-27 16:20:46 +02:00
Ricki Hirner
96adb926c6 Use ical4j 3.x 2020-05-27 16:20:46 +02:00
Ricki Hirner
14bdaaa189 minor change in evil manufacturer warning in intro fragment 2020-05-27 16:15:19 +02:00
Ricki Hirner
86bb432625 Fix intro fragment checkbox/button interaction 2020-05-27 16:15:16 +02:00
Ricki Hirner
97d657e501 Fix crash because of empty drawable in -ose version 2020-04-27 12:04:09 +02:00
Ricki Hirner
4067afb20d Fix build 2020-04-24 21:04:43 +02:00
Ricki Hirner
843f1e099b Merge branch 'dev-3.x-ose' into master-ose 2020-04-22 13:16:02 +02:00
Ricki Hirner
4ed9d8a6a8 F-Droid changelog for 3.0 2020-04-22 13:15:19 +02:00
Ricki Hirner
5b60ccaa2e Merge branch 'dev-3.x-ose' into master-ose 2020-04-22 11:53:27 +02:00
Ricki Hirner
9bdfcb5dc1 Update AboutLibraries, bump version to 3.0 2020-04-22 11:49:28 +02:00
Ricki Hirner
fd92267c4d Replace stock images by images from undraw.co 2020-04-21 19:19:20 +02:00
Ricki Hirner
3c100274ad Fetch translations from Transifex 2020-04-21 18:49:46 +02:00
Ricki Hirner
ede9f2a472 cert4android: update strings 2020-04-21 18:49:46 +02:00
Ricki Hirner
884dbdbd3d Adapt WebView progress bar; don't use okhttp BOM 2020-04-21 18:49:46 +02:00
Ricki Hirner
e267e620ba Fix build; bump version to 3.0-beta2 2020-04-21 18:49:46 +02:00
Ricki Hirner
f32006254f Nextcloud Login flow: show progress and errors (including TLS errors) 2020-04-21 18:49:46 +02:00
Ricki Hirner
2f92002fc3 Update dependencies; clean up ProGuard/R8 rules 2020-04-21 18:49:46 +02:00
Ricki Hirner
a7c814bafd Use new AboutLibraries gradle plugin 2020-04-21 18:49:46 +02:00
Ricki Hirner
e124ce2f70 Update gradle plugin 2020-04-21 18:49:46 +02:00
Ricki Hirner
17c9537fc5 Adapt login styles 2020-04-21 18:49:46 +02:00
Ricki Hirner
ad4c7c97c2 Don't cancel notifications of other sync threads (lets important notifications disappear sometimes) 2020-04-21 18:49:46 +02:00
Ricki Hirner
27675de7ec Version bump to 3.0-beta1 2020-04-21 18:49:46 +02:00
Ricki Hirner
204ca9120d Update About activity 2020-04-21 18:49:46 +02:00
Ricki Hirner
061df6a014 Enable Brotli (in addition to gzip) compression 2020-04-21 18:49:46 +02:00
Ricki Hirner
aab676b7e4 Code cleanup 2020-04-21 18:49:46 +02:00
Ricki Hirner
7fd2a99edb Keep TLS 1.0 and 1.1 for now; update dependencies 2020-04-21 18:49:46 +02:00
Ricki Hirner
b7893398bd Fix some vector icons 2020-04-21 18:49:46 +02:00
Ricki Hirner
07f2c85ae1 Remove bitmap notification icons 2020-04-21 18:49:45 +02:00
Ricki Hirner
08cefe9f80 Fix tests 2020-04-21 18:49:45 +02:00
Ricki Hirner
0b9aeb05ab Update okhttp to 4.5.0 and dav4jvm to 2.0 2020-04-21 18:49:45 +02:00
Ricki Hirner
1d7e4e8d33 Revert "Update libraries (including dnsjava 2.x -> 3.x)"
This reverts commit bcd7700dc7d2d67a6c3a25c8f9a18694e496a29c.
2020-04-21 18:49:45 +02:00
Ricki Hirner
621c32da2a Remove vectorDrawables support library and multidex 2020-04-21 18:49:45 +02:00
Ricki Hirner
dcb3dd577a Require Android 5 (SDK level 21); update gradle
Recent okhttp versions require Android 5. Also, a lot of workarounds
(like non-vector graphics for notification icons) which are cumbersome
to maintain have accumulated.

So, DAVx5 will require Android 5 (SDK level 21) from the next release,
which will probably tagged as 3.0.

2.6.x (currently 2.6.5) will be the last branch which supports Android 4.4.
Maybe there will never be a version after 2.6.5, which will still be offered for
Android 4.4 devices. If there's big need for a maintenance release (like severe security
problems), there may be further 2.6.x releases (branched from 2.6.5), but
development and new features will only go to the master branch.
2020-04-21 18:49:45 +02:00
Ricki Hirner
a07d849c35 Version bump to 2.7.0-beta2 2020-04-21 18:49:45 +02:00
Ricki Hirner
cea8d54556 lint; remove soldupe for now 2020-04-21 18:49:45 +02:00
Ricki Hirner
6189eef55f Update libraries (including dnsjava 2.x -> 3.x)
* dnsjava 3.x is now compatible with Android by default; removed compatibility code
2020-04-21 18:49:45 +02:00
Ricki Hirner
07dc7592b7 Minor code cleanup 2020-04-21 18:49:45 +02:00
Ricki Hirner
da42ad63a2 Intro optimizations 2020-04-21 18:49:45 +02:00
Ricki Hirner
a2eabbdcee battery intro: capitalize manufacturer and re-check on activity resume; update dependencies 2020-04-21 18:49:45 +02:00
Ricki Hirner
ad185865c4 Version bump to 2.7.0-beta1 2020-04-21 18:49:45 +02:00
Ricki Hirner
b0a5bfccad Introduce IntroActivity instead of startup fragments 2020-04-21 18:49:45 +02:00
Ricki Hirner
4303132a38 Fetch translations from Transifex 2020-04-21 18:48:47 +02:00
Ricki Hirner
a0da93f910 cert4android: update strings 2020-04-21 18:48:47 +02:00
Ricki Hirner
3c38f063c5 Adapt WebView progress bar; don't use okhttp BOM 2020-04-21 18:48:47 +02:00
Ricki Hirner
c18ef6304b Fix build; bump version to 3.0-beta2 2020-04-21 18:48:47 +02:00
Ricki Hirner
2542aba3f0 Nextcloud Login flow: show progress and errors (including TLS errors) 2020-04-21 18:48:47 +02:00
Ricki Hirner
75ef28bd1d Update dependencies; clean up ProGuard/R8 rules 2020-04-21 18:48:47 +02:00
Ricki Hirner
3aa729051f Use new AboutLibraries gradle plugin 2020-04-21 18:48:47 +02:00
Ricki Hirner
49fafb40e3 Update gradle plugin 2020-04-21 18:48:47 +02:00
Ricki Hirner
d1dcf889f3 Adapt login styles 2020-04-21 18:48:47 +02:00
Ricki Hirner
91f9baf042 Don't cancel notifications of other sync threads (lets important notifications disappear sometimes) 2020-04-21 18:48:47 +02:00
Ricki Hirner
453e703bc0 Version bump to 3.0-beta1 2020-04-21 18:48:47 +02:00
Ricki Hirner
0d5b3c2816 Update About activity 2020-04-21 18:48:47 +02:00
Ricki Hirner
86744db3b3 Enable Brotli (in addition to gzip) compression 2020-04-21 18:48:47 +02:00
Ricki Hirner
2965ae4b1e Code cleanup 2020-04-21 18:48:47 +02:00
Ricki Hirner
3a1edbafbf Keep TLS 1.0 and 1.1 for now; update dependencies 2020-04-21 18:48:47 +02:00
Ricki Hirner
7f5f3e492b Fix some vector icons 2020-04-21 18:48:47 +02:00
Ricki Hirner
e7dce774c8 Remove bitmap notification icons 2020-04-21 18:48:47 +02:00
Ricki Hirner
25b97e96e1 Fix tests 2020-04-21 18:48:47 +02:00
Ricki Hirner
0d66d29380 Update okhttp to 4.5.0 and dav4jvm to 2.0 2020-04-21 18:48:47 +02:00
Ricki Hirner
029c4737bd Revert "Update libraries (including dnsjava 2.x -> 3.x)"
This reverts commit bcd7700dc7d2d67a6c3a25c8f9a18694e496a29c.
2020-04-21 18:48:47 +02:00
Ricki Hirner
5e5d59889f Remove vectorDrawables support library and multidex 2020-04-21 18:48:47 +02:00
Ricki Hirner
bc9aaf04fe Require Android 5 (SDK level 21); update gradle
Recent okhttp versions require Android 5. Also, a lot of workarounds
(like non-vector graphics for notification icons) which are cumbersome
to maintain have accumulated.

So, DAVx5 will require Android 5 (SDK level 21) from the next release,
which will probably tagged as 3.0.

2.6.x (currently 2.6.5) will be the last branch which supports Android 4.4.
Maybe there will never be a version after 2.6.5, which will still be offered for
Android 4.4 devices. If there's big need for a maintenance release (like severe security
problems), there may be further 2.6.x releases (branched from 2.6.5), but
development and new features will only go to the master branch.
2020-04-21 18:48:47 +02:00
Ricki Hirner
3ba2922bde Version bump to 2.7.0-beta2 2020-04-21 18:48:47 +02:00
Ricki Hirner
6277f35db4 lint; remove soldupe for now 2020-04-21 18:48:47 +02:00
Ricki Hirner
b0c53fb852 Update libraries (including dnsjava 2.x -> 3.x)
* dnsjava 3.x is now compatible with Android by default; removed compatibility code
2020-04-21 18:48:47 +02:00
Ricki Hirner
ce0e623912 Minor code cleanup 2020-04-21 18:48:47 +02:00
Ricki Hirner
d1709df0b6 Intro optimizations 2020-04-21 18:48:47 +02:00
Ricki Hirner
5469dee1f2 battery intro: capitalize manufacturer and re-check on activity resume; update dependencies 2020-04-21 18:48:47 +02:00
Ricki Hirner
489f7ac639 Version bump to 2.7.0-beta1 2020-04-21 18:48:47 +02:00
Ricki Hirner
58ca99198f Introduce IntroActivity instead of startup fragments 2020-04-21 18:48:47 +02:00
Ricki Hirner
8a46fcedba Do full resync if "past event time limit" is changed from number to null 2020-03-29 17:57:25 +02:00
Ricki Hirner
a0816c11d2 Show languages in About 2020-03-07 18:51:02 +01:00
Ricki Hirner
c06950751b Version bump to 2.6.5 2020-03-07 12:30:52 +01:00
Ricki Hirner
cfc0130bec Version bump to 2.6.5-beta1; update dependencies 2020-03-06 12:08:36 +01:00
Alex Baker
213851856e Clear password error on text changed 2020-03-06 12:04:48 +01:00
Ricki Hirner
b7e60cd143 Only use multi-get for tasks sync, too 2020-03-06 12:04:48 +01:00
Ricki Hirner
37a299d0f7 Fetch translations from Transifex 2020-03-06 00:23:42 +01:00
Ricki Hirner
41d33fac44 Version bump to 2.6.5 2020-03-06 00:22:36 +01:00
Ricki Hirner
3dc2aa65df Sync algorithm bug fixes/improvements
- Collection sync: don't save new sync state before downloading is finished
- throw exception when waiting for completion times out
- always use multi-get, even for single vCards/iCalendars
2020-03-06 00:06:07 +01:00
Ricki Hirner
43f4d9c05d Merge branch 'textlayout_errors' into 'master-ose'
Move errors from EditTexts to TextInputLayouts

See merge request bitfireAT/davx5-ose!28
2020-03-03 21:38:09 +00:00
Ricki Hirner
6c0b555ec9 lint 2020-03-03 18:59:33 +01:00
Ricki Hirner
8918382003 Use requireView() instead of view!! and requireActivity() instead of activity!! 2020-03-03 16:28:32 +01:00
Ricki Hirner
e30c41828f Update gradle plugin, okhttp 2020-03-03 14:46:00 +01:00
Ricki Hirner
224c92cc87 Update gradle version and Android plugin; dependencies 2020-03-03 14:46:00 +01:00
Ricki Hirner
4498f9bf03 Update okhttp to 3.12.9 2020-03-03 14:45:52 +01:00
Alex Baker
b3c7f1f9ef Move errors from EditTexts to TextInputLayouts 2020-02-27 08:57:07 -06:00
Ricki Hirner
5622d743c3 Version bump to 2.6.4 2020-02-20 18:31:01 +01:00
Ricki Hirner
ae4c6e94c8 Fetch translations from Transifex 2020-02-20 18:15:50 +01:00
Ricki Hirner
7bdb5c5a97 Update cert4android 2020-02-20 18:13:57 +01:00
Ricki Hirner
62c0dfbaee Update support libraries 2020-02-20 18:10:57 +01:00
Ricki Hirner
53f35d5ee3 Event sync: delete exceptions from events when events are mass-deleted, too 2020-02-20 18:10:57 +01:00
Ricki Hirner
c9dab31067 Webcal calendars: use UrlUtils.equals to find matching calendar; update dependencies 2020-01-24 20:22:02 +01:00
Ricki Hirner
ac3e9fb825 Fetch translations from Transifex 2020-01-23 18:39:07 +01:00
Ricki Hirner
8325fdcf86 Version bump to 2.6.4-beta1 2020-01-23 18:37:41 +01:00
Ricki Hirner
3f2090ecc7 Update ical4android (alarm handling) and AboutLibraries 2020-01-20 22:17:19 +01:00
Ricki Hirner
e236b07184 Use okhttp version defined by DAVx5 (dav4jvm version may be older) 2020-01-16 19:57:38 +01:00
Ricki Hirner
3905679576 Version bump to 3.6.2.1 2020-01-14 21:11:58 +01:00
Ricki Hirner
2f33374649 Update dependencies (including okhttp 3.12.8) 2020-01-14 21:08:49 +01:00
Ricki Hirner
54222d3328 Version bump to 2.6.3; add F-Droid changelog 2020-01-07 14:47:55 +01:00
Ricki Hirner
31339f3014 Don't create default reminder for full-day events 2020-01-07 09:47:32 +01:00
Ricki Hirner
6f1d513e54 Version bump to 2.6.3-beta6 2020-01-06 20:59:44 +01:00
Ricki Hirner
fef8ce2366 Improve resource detection: detect address books/calendars when they are identical with their home-set 2020-01-06 16:25:39 +01:00
Ricki Hirner
c9ca84f14b Account activity: make space for FAB at end of collection list 2020-01-06 14:40:30 +01:00
Ricki Hirner
53698adda2 Version bump to 2.6.3-beta5 2020-01-06 13:50:29 +01:00
Ricki Hirner
27c89e762e Fetch translations from Transifex 2020-01-06 13:49:35 +01:00
Ricki Hirner
92394bb6af Take default reminder from settings provider, if available 2020-01-06 13:39:46 +01:00
Ricki Hirner
eba5280a58 Update ical4android, okhttp 2020-01-05 13:58:16 +01:00
Ricki Hirner
c3157f1256 Bump version to 2.6.3-beta4 2019-12-30 17:35:50 +01:00
Ricki Hirner
5c795950af Fetch translations from Transifex 2019-12-30 17:28:28 +01:00
Ricki Hirner
15f5152a7d UiUtils.launchUri: show toast if no browser is installed 2019-12-28 15:33:59 +01:00
Ricki Hirner
03f54f5402 Add link to Privacy policy to Accounts drawer 2019-12-28 15:16:03 +01:00
Ricki Hirner
41ba92cc4c Default alarms: don't take app-wide Settings into account 2019-12-27 23:22:27 +01:00
Ricki Hirner
c267c92a87 Rewrite account settings to use ViewModel 2019-12-27 19:00:33 +01:00
Ricki Hirner
3111e54d1a Rename "default alarm" to "default reminder" 2019-12-27 19:00:19 +01:00
Ricki Hirner
ebd20866b3 Version bump to 2.6.3-beta3 2019-12-26 17:51:07 +01:00
Ricki Hirner
a789246925 Account settings: always call sync adapter for re-sync 2019-12-26 17:49:12 +01:00
Ricki Hirner
11452caa13 cert4android: use new Material theme 2019-12-26 17:26:29 +01:00
Ricki Hirner
7577eb10e8 Rename SyncAdapterService.SYNC_EXTRAS_RELOAD_ALL to SYNC_EXTRAS_FULL_RESYNC 2019-12-26 17:26:29 +01:00
Ricki Hirner
4b579ca419 Introduce default alarm setting
* move .ui.AccountSettingsActivity to .ui.account.SettingsActivity
* add setting for default alarms
* add sync extra: SyncAdapterService.SYNC_EXTRAS_RELOAD_ALL (forces full re-synchronization of all members)
* LocalCollection: add forgetETags() which resets the ETags of members
* account settings: automatic reloading of members when certain settings are modified
2019-12-26 17:26:29 +01:00
Ricki Hirner
f9a8de29e2 Fix vcard4android; bump version to 2.6.3-beta2 2019-12-23 22:42:07 +01:00
Ricki Hirner
d28f5115fb vCard sync: improve compatibility (for instance with Samsung "Edge panel"); version bump to 2.6.3-beta1 2019-12-22 17:24:52 +01:00
Ricki Hirner
2a40432494 Fetch translations from Transifex 2019-12-22 11:03:28 +01:00
Ricki Hirner
06d79087b0 Version bump to 2.6.2 2019-12-22 11:01:32 +01:00
Ricki Hirner
3e6243901e Version bump to 2.6.2-beta5 2019-12-20 17:14:52 +01:00
Ricki Hirner
054955b89e Use String for SYNC_EXTRAS_PRIORITY_COLLECTIONS becuase LongArray mustn't be used for sync extras 2019-12-20 12:14:39 +01:00
Ricki Hirner
c28001f0f1 Version bump to 2.6.2-beta4 2019-12-17 14:01:27 +01:00
Ricki Hirner
c4d7e6857b CardDAV: handle (non-standard) TYPE=other for phone numbers and addresses, too (for better compatibility) 2019-12-17 13:34:01 +01:00
Ricki Hirner
4f237d82fb Update theme 2019-12-17 12:55:24 +01:00
Ricki Hirner
05cb460cf5 Version bump to 2.6.2-beta3 2019-12-15 22:22:26 +01:00
Ricki Hirner
2a31ebb2a3 Increase vCard compatibility 2019-12-15 22:14:51 +01:00
Ricki Hirner
372ad8b704 SYNC_EXTRAS_PRIORITY_COLLECTIONS: use LongArray instead of String 2019-12-13 11:06:55 +01:00
Ricki Hirner
296f55651e Introduce SYNC_EXTRAS_PRIORITY_COLLECTIONS; ical4android: map event categories to special extended property 2019-12-12 20:15:30 +01:00
Ricki Hirner
fe5c5737ef Update gradle plugin, version bump to 2.6.2-beta2 2019-12-11 12:48:31 +01:00
Ricki Hirner
32fe98a196 ProGuard: keep enum classes 2019-12-07 23:48:48 +01:00
Ricki Hirner
de687eaf52 Version bump to 2.6.2-beta1 2019-12-06 12:50:37 +01:00
Ricki Hirner
2062802cb6 Improve usage of Material theme 2019-12-03 18:52:35 +01:00
Ricki Hirner
b280941b2b Don't handle uncaught exceptions in debug builds 2019-12-03 18:52:33 +01:00
Ricki Hirner
049145d0ea use Material theme; update AboutLibraries dependency 2019-12-03 18:51:47 +01:00
Ricki Hirner
c1162fce8e Library updates; show permissions in debug info 2019-11-30 23:50:42 +01:00
Ricki Hirner
40fd412064 Fetch translations from Transifex 2019-11-25 22:58:38 +01:00
Ricki Hirner
c13243f3ae Version bump to 2.6.1.1 2019-11-25 22:58:38 +01:00
Ricki Hirner
bda2d64ca4 ical4android: generate DTSTAMP again 2019-11-25 22:57:16 +01:00
Ricki Hirner
f974ca2ffa ical4android: unify VEVENT/VTODO parsing 2019-11-24 15:35:42 +01:00
Ricki Hirner
d6cd0faeeb ical4android: better compatibility with Outlook timezone IDs 2019-11-24 14:38:44 +01:00
Ricki Hirner
33345db0f0 Managed DAVx5: don't show OpenTasks startup dialog 2019-11-23 14:52:33 +01:00
Ricki Hirner
6e2d7b10d7 Fetch translations from Transifex 2019-11-22 18:04:10 +01:00
Ricki Hirner
b9fb983b97 Version bump to 2.6.1 2019-11-22 18:02:55 +01:00
Ricki Hirner
fe738636a2 Events: ignore empty strings when processing locally stored events; tasks: clear parent_id when it's not set anymore after a remote update 2019-11-16 18:57:41 +01:00
Ricki Hirner
46df7a63c7 Version bump to 2.6.1-beta4 2019-11-16 16:21:57 +01:00
Ricki Hirner
b4ebfa4fe5 SyncManager: generate all UID/file names before uploading any resources
- provider queries: use boolean syntax ("WHERE dirty" instead of "WHERE dirty!=0" etc.)
2019-11-16 16:21:39 +01:00
Ricki Hirner
9979dc95c2 Fetch translations from Transifex 2019-11-15 23:48:45 +01:00
Ricki Hirner
ebf1273c37 Version bump to 2.6.1-beta3 2019-11-15 23:47:59 +01:00
Ricki Hirner
3bfe33e37a Correctly handle EMAIL reminders 2019-11-15 23:47:41 +01:00
Ricki Hirner
6d273f8b0d Account settings migration: download all tasks again to parse relations etc. 2019-11-13 17:17:23 +01:00
Ricki Hirner
6922a68b77 Version bump to 2.6.1-beta2 2019-11-13 14:12:15 +01:00
Ricki Hirner
af3a588632 Tasks: directly use Relation rows, no need for DelayedRelation anymore 2019-11-13 14:11:58 +01:00
Ricki Hirner
285dfe9307 Fetch translations from Transifex 2019-11-13 00:04:00 +01:00
Ricki Hirner
bc80e74a83 Version bump to 2.6.1-beta1 2019-11-13 00:03:17 +01:00
Ricki Hirner
6fe5fafed7 Tasks: support RELATED-TO (subtasks) 2019-11-13 00:02:55 +01:00
Ricki Hirner
058354b9a4 Update dependencies 2019-11-12 14:02:56 +01:00
Ricki Hirner
16e1d5041f Dokka: add links to source code and libraries 2019-11-12 14:02:56 +01:00
Ricki Hirner
4bca002892 ical4android: save/restore unknown properties of tasks; disable allowBackup because it won't work anyway 2019-11-11 00:10:30 +01:00
Ricki Hirner
fba759583d Login with URL: assume https:// URI scheme if none given 2019-11-10 10:36:12 +01:00
Ricki Hirner
3ed16ae5b2 Add CONTRIBUTING 2019-11-09 22:06:47 +01:00
Ricki Hirner
888ffc8d90 Require privileged container for tests 2019-11-09 19:52:18 +01:00
Ricki Hirner
5ff90d8e20 Update dependencies 2019-11-09 19:52:04 +01:00
Ricki Hirner
edab897732 Use headless emulator from new repo for testing 2019-11-08 00:57:01 +01:00
Ricki Hirner
06637b4b47 Update dependencies 2019-11-08 00:56:56 +01:00
Ricki Hirner
2ff839837a Fetch translations from Transifex 2019-10-25 11:09:35 +02:00
Ricki Hirner
e80243b133 Version bump to 2.6 2019-10-25 11:08:55 +02:00
Ricki Hirner
481eccc4d6 Login Flow: optimization 2019-10-24 11:21:35 +02:00
Ricki Hirner
2e641a9e9b Merge branch 'fixLoginFlow' into 'master-ose'
Nextcloud login flow: correct concatenate url with davPath

See merge request bitfireAT/davx5-ose!22
2019-10-24 08:48:21 +00:00
Ricki Hirner
fe8b4b1d24 Merge branch 'fixUsername' into 'master-ose'
username was retrieved from intent, but not used

See merge request bitfireAT/davx5-ose!23
2019-10-24 08:44:13 +00:00
tobiasKaminsky
2fc9d2862e username was retrieved from intent, but not used 2019-10-22 09:48:01 +02:00
tobiasKaminsky
23a9be403b correct concatenate url with davPath.
Previously a subfolder was omitted:
serverUrl: http://localhost/nc
davPath: /remote.php/dav
-->
wrong: http://localhost/remote.php/dav
correct: http://localhost/nc/remote.php/dav
2019-10-22 09:37:09 +02:00
Ricki Hirner
cd9518c619 Version bump to 2.6-beta3 2019-10-09 11:04:28 +02:00
Ricki Hirner
29c0a9b586 Account list fragment network detection: compatibility with Android <6 2019-10-08 19:07:57 +02:00
Ricki Hirner
aac25b3bdc Connectivity check: check for VALIDATED INTERNET capability 2019-10-07 17:17:35 +02:00
Ricki Hirner
31b47a8554 Fetch translations from Transifex 2019-10-07 11:45:34 +02:00
Ricki Hirner
f8eb7a6d56 cert4android: fetch translations from Transifex 2019-10-07 11:43:53 +02:00
Ricki Hirner
f41786f9f1 Version bump to 2.6-beta1 2019-10-07 11:43:52 +02:00
Ricki Hirner
80f31bbc03 Implement Nextcloud Login Flow 2019-10-07 11:37:13 +02:00
Ricki Hirner
4c229c81a1 Account list fragment: Show "Network unavailable" message if there is no Internet connection (only API 21+) 2019-10-07 11:35:05 +02:00
Ricki Hirner
0ac6dc8538 Set target SDK to level 29 (Android 10)
- ask for ACCESS_FINE_LOCATION/ACCESS_BACKGROUND_LOCATION when sync is restricted to specific WiFi SSIDs
- use Android 5+ way to determine active network connections, if possible
2019-09-30 17:28:11 +02:00
Ricki Hirner
1a52794bd1 Keep "force read only" flag when refreshing collections 2019-09-30 15:36:52 +02:00
Ricki Hirner
5f9f5e2732 Get dav4jvm from jitpack instead of using a submodule 2019-09-30 13:45:34 +02:00
Ricki Hirner
9c50903fd6 Fetch translations from Transifex 2019-09-21 11:48:08 +02:00
Ricki Hirner
aa4b3657e5 Version bump to 2.5.5 2019-09-21 11:46:33 +02:00
Ricki Hirner
ed23b4834c Fetch translations from Transifex (thanks for Bulgarian and Silesian!) 2019-09-20 00:10:18 +02:00
Ricki Hirner
da81482bad lint 2019-09-19 14:27:27 +02:00
Ricki Hirner
f453d06929 Minor method signature updates for SDK level 29 2019-09-19 13:58:28 +02:00
Ricki Hirner
0d07fe9020 Version bump to 2.5.5-beta1, compile against SDK level 29 2019-09-19 13:43:30 +02:00
Ricki Hirner
ddd3ffb2c2 Remove conscrypt version (set by cert4android) 2019-09-19 13:43:03 +02:00
Ricki Hirner
b4b87bc4f5 Update cert4android 2019-09-18 23:40:24 +02:00
Ricki Hirner
e74a919812 Update dependencies; new findPreference syntax 2019-09-18 23:40:24 +02:00
Ricki Hirner
ef8931c9c4 Tasks: support CATEGORIES 2019-09-18 23:40:24 +02:00
Ricki Hirner
13e9d7c7b4 Enable Autofill for login screen 2019-08-25 16:35:43 +02:00
Ricki Hirner
4c4cbf6f2e Version bump to 2.5.4.1 2019-08-25 15:32:05 +02:00
Ricki Hirner
55a51c0b87 Fetch translations from Transifex 2019-08-25 15:31:01 +02:00
Ricki Hirner
e7833e403f Update gradle plugin 2019-08-25 15:29:27 +02:00
Ricki Hirner
ee36b10fd4 Add ProGuard line to avoid AppCompat crash 2019-08-13 18:59:57 +02:00
Ricki Hirner
79c4e24816 Version bump to 2.5.4 2019-08-06 12:45:47 +02:00
Ricki Hirner
4a8bf47aa3 Update material components to 1.1.0-alpha09, allegedly fixes Meizu crash 2019-08-05 22:57:02 +02:00
Ricki Hirner
d32a142ef0 Fetch translations from Transifex 2019-07-20 22:12:18 +02:00
Ricki Hirner
1a8171a55e Version bump to 2.5.3 2019-07-20 22:07:37 +02:00
Ricki Hirner
d5a03d7837 Update dependencies 2019-07-19 14:32:47 +02:00
Ricki Hirner
bcc8e02d77 Create collections: enable sync of created collections by default 2019-07-19 14:30:55 +02:00
Ricki Hirner
75124d99bc Home set enumeration: use correct displayName, also honor bind privilege; version bump to 2.5.3-beta1 2019-07-19 14:30:55 +02:00
Ricki Hirner
a081ef210b Re-enable HTTP/2 again (was disabled for all connections and not only when using client certificates by mistake) 2019-07-19 14:30:55 +02:00
Ricki Hirner
1882dee0c2 Process home-set display names 2019-07-16 12:52:41 +02:00
Ricki Hirner
fb7012e7c6 Update gradle plugin 2019-07-12 22:48:10 +02:00
Ricki Hirner
bbb32d501f Fetch translations from Transifex 2019-07-10 14:57:29 +02:00
Ricki Hirner
338375789a Fetch translations from Transifex 2019-07-08 21:46:08 +02:00
Ricki Hirner
123c5906bd Update ical4j; version bump to 2.5.2 2019-07-08 16:45:54 +02:00
devvv
eeac458d93 Update README.md 2019-06-24 17:18:14 +00:00
Ricki Hirner
471722496e ical4android: fix threading problem 2019-06-23 15:53:53 +02:00
Ricki Hirner
1ecd16c229 Address books accounts: set initial user data twice for older Android versions 2019-06-20 12:26:19 +02:00
Ricki Hirner
1804c4af96 Debug info: also show UnknownHostException; update Room dependency 2019-06-19 01:06:27 +02:00
Ricki Hirner
5974ffc7f6 Fetch translations from Transifex 2019-06-09 11:18:31 +02:00
Ricki Hirner
5073a56fd6 Version bump to 2.5.1 2019-06-09 11:18:31 +02:00
Ricki Hirner
1e76a92d02 Version bump to 2.5.1-beta2 2019-06-07 21:26:56 +02:00
Ricki Hirner
5709fd38d9 Webcal subscriptions: handle webcal(s):// URLs and treat them as http(s):// 2019-06-07 21:26:31 +02:00
Ricki Hirner
4bf9ba5f2d Update dependencies 2019-06-07 21:26:31 +02:00
Ricki Hirner
f5e317a08a Fetch translations from Transifex 2019-05-29 18:12:07 +02:00
Ricki Hirner
8936ad7810 Update dokka, Kotlin 2019-05-29 18:10:14 +02:00
Ricki Hirner
1d20dbe4e8 Version bump to 2.5.1-beta1 2019-05-29 18:05:58 +02:00
Ricki Hirner
b247e70d2a Use android:hint in TextInputLayout instead of TextInputEditText (should fix Meizu crashes) 2019-05-29 18:05:31 +02:00
Ricki Hirner
477ee085fc Show unhandled exceptions with DebugInfoActivity 2019-05-25 11:14:45 +02:00
Ricki Hirner
8f62c185ec Don't use HTTP/2 with client certificates 2019-05-22 23:58:00 +02:00
Ricki Hirner
f6144dc0ab Update gradle plugin, library dependencies 2019-05-17 12:04:03 +02:00
Ricki Hirner
a958ed48f9 AccountActivity: set color for swipe refresh 2019-05-15 11:52:21 +02:00
Ricki Hirner
3458f786f5 Fetch translations from Transifex; add Finnish 2019-05-15 11:43:56 +02:00
Ricki Hirner
db037f1dfb Bump version code to 288 2019-05-15 11:39:43 +02:00
Ricki Hirner
c4ee6037ec Don't show permantenly pending sync for CalDAV if OpenTasks is not installed 2019-05-14 21:12:56 +02:00
Ricki Hirner
82310b2c0d Fetch translations from Transifex 2019-05-13 20:20:09 +02:00
Ricki Hirner
292b72b62f Version bump to 2.5; minor lint fixes 2019-05-13 20:13:08 +02:00
Ricki Hirner
4cdf26d674 cert4android: update translations 2019-05-10 16:47:07 +02:00
Ricki Hirner
c5c68ad7c9 Fetch translations from Transifex; new translation: Slovak (thanks brango67!) 2019-05-10 16:46:51 +02:00
Ricki Hirner
c734e1a1f2 Debug info: set informational text for shared logs 2019-05-09 21:21:56 +02:00
Ricki Hirner
459f9da486 Fetch translations from Transifex 2019-05-09 12:22:12 +02:00
Ricki Hirner
1fbd532237 Version bump to 2.5-beta2 2019-05-09 12:17:21 +02:00
Ricki Hirner
a67a56e0c5 Fix NPE in WebcalFragment 2019-05-09 12:16:57 +02:00
Ricki Hirner
787c5e480a Update icon resolutions, names and action bar icon color 2019-05-09 12:16:48 +02:00
Ricki Hirner
38ecd7a4c4 Update libraries 2019-05-09 12:16:41 +02:00
Ricki Hirner
17cf4a6289 Version bump to 2.5-beta1 2019-05-06 21:42:11 +02:00
Ricki Hirner
ac8558ce6a Progress bars for AccountActivity (which now also show pending syncs) 2019-05-06 21:41:42 +02:00
Ricki Hirner
4db40ac223 Fetch translations from Transifex 2019-05-06 19:35:54 +02:00
Ricki Hirner
38ab785382 Update ical4j: now allows UTC properties to have non-UTC values for compatibility 2019-05-06 19:23:30 +02:00
Ricki Hirner
0c5bb19be9 Minor fixes 2019-05-06 19:20:22 +02:00
Ricki Hirner
310e5a1720 More AccountActivity; fix bug in CreateCalendarActivity & co 2019-05-06 19:20:15 +02:00
Ricki Hirner
9df0db1ec3 Correctly handle permissions in WebcalFragment 2019-05-06 19:20:08 +02:00
Ricki Hirner
f6f3ebb48b DavService: don't delete all homesets/collections on refresh, but update only changed rows 2019-05-06 19:20:01 +02:00
Ricki Hirner
ffe59915d9 Rename AccountActivity2 to AccountActivity 2019-05-06 19:19:54 +02:00
Ricki Hirner
94a8c4c218 Make AccountActivity2 fully working again 2019-05-06 19:19:46 +02:00
Ricki Hirner
efa168b941 Use AppBarLayout for AccountActivity 2019-05-06 19:19:38 +02:00
Ricki Hirner
b8abc60752 Linuxtage Action 2019-05-06 19:19:01 +02:00
Ricki Hirner
77d6fc7243 Fix Android 4.4 compatibility; version code bump 2019-04-19 14:15:49 +02:00
Ricki Hirner
f50f86c1ad vCard: use "groupX" instead of "davdroidX" for property grouping 2019-04-19 12:57:55 +02:00
Ricki Hirner
3c07bc3abc Use Conscrypt; show progress bar when loading account activity 2019-04-19 02:33:01 +02:00
Ricki Hirner
400b7babbc Improve database migrations 2019-04-18 13:35:29 +02:00
Ricki Hirner
5ff86d809f Fix crash in account setup 2019-04-18 13:35:29 +02:00
Ricki Hirner
9af246a01a Version bump to 2.4.1-beta1 2019-04-18 13:25:39 +02:00
Ricki Hirner
d7cefa84f4 Update Kotlin, gradle, gradle plugin 2019-04-17 22:53:19 +02:00
Ricki Hirner
d7b73a35e1 Tests 2019-04-17 12:24:33 +02:00
Ricki Hirner
f20d921559 Handle permissions with LiveData; manifest: add app settings/debug info activity 2019-04-16 21:36:14 +02:00
Ricki Hirner
cdec545568 Use live paged data for AccountActivity 2019-04-16 21:36:14 +02:00
Ricki Hirner
6af50dbc43 Use Room for database 2019-04-16 21:36:14 +02:00
Ricki Hirner
7a44f61887 Minor cert4android/ical4android update 2019-04-03 00:00:53 +02:00
Ricki Hirner
4ec98acdd1 Fetch translations from Transifex 2019-04-02 23:48:55 +02:00
Ricki Hirner
e0a000cbd8 Hotfix: AccountActivity: avoid endless loop of asking for permissions; version bump to 2.4.0.1 2019-04-02 23:48:14 +02:00
Ricki Hirner
7c8f74ab14 Version bump to 2.4 2019-03-29 15:07:42 +01:00
Ricki Hirner
03cbe4f665 Update read-only state or raw contacts/data rows according to the address book 2019-03-27 12:34:44 +01:00
Ricki Hirner
a76829c3dd Version bump to 2.4-beta4 2019-03-27 12:15:20 +01:00
Ricki Hirner
d83a4df524 Account activity: update "force read only" responsively 2019-03-27 12:13:21 +01:00
Ricki Hirner
afdfb735bd Fetch translations from Transifex 2019-03-26 22:02:48 +01:00
Ricki Hirner
414f771156 Remove unused sync status notification channel 2019-03-26 22:00:03 +01:00
Ricki Hirner
c690dfd148 Support read-only flag in raw contacts/data rows for read-only address books 2019-03-26 21:11:56 +01:00
Ricki Hirner
50775f26ce Change secondary text color to darker gray 2019-03-26 18:19:06 +01:00
Ricki Hirner
40a1748461 Use .closeCompat for ContentProviderClient instances 2019-03-26 18:19:06 +01:00
Ricki Hirner
63e360fb3a AccountActivity: remove "Select collections to synchronize" 2019-03-26 13:01:33 +01:00
Ricki Hirner
d7216f5ffa Update libraries; bump version to 2.4-beta3 2019-03-26 12:14:53 +01:00
Ricki Hirner
a749a9fd34 Rename "debug model" (artifact from search/replace) to "debug info" again 2019-03-26 12:14:25 +01:00
Ricki Hirner
6951397945 AccountActivity: use ViewModel, RecyclerView 2019-03-25 11:37:44 +01:00
Ricki Hirner
6153b3aafe AccountAdapter not-null assertion 2019-03-24 17:17:37 +01:00
Ricki Hirner
325dd41820 Version bump to 2.4-beta2 2019-03-24 00:05:59 +01:00
Ricki Hirner
be94ee19d8 Enable gradle incubation 2019-03-23 23:27:17 +01:00
Ricki Hirner
d03ef78db9 Update tests 2019-03-23 23:24:37 +01:00
Ricki Hirner
e7e580132f Enable Gitlab CI again 2019-03-23 23:24:37 +01:00
Ricki Hirner
a7c62d11ca Update libs, About logo 2019-03-23 22:54:38 +01:00
Ricki Hirner
d978e6a17d Fetch translations from Transifex 2019-03-23 21:19:20 +01:00
Ricki Hirner
87457bf6d4 Small fixes 2019-03-23 21:19:20 +01:00
Ricki Hirner
bff9c87c1f Create collections: use ViewModel 2019-03-23 20:53:49 +01:00
Ricki Hirner
d27261f18a Handle exceptions when acquiring task provider 2019-03-23 20:52:09 +01:00
Ricki Hirner
affce28652 Don't rely on Manifest-declared implicit broadcast to detect when a tasks app is being installed/removed (Android 8 compatibility) 2019-03-23 20:50:28 +01:00
Ricki Hirner
b319fef88a Version bump to 2.4-beta1 2019-03-23 20:37:18 +01:00
Ricki Hirner
6369ae1d36 About: use vector icon 2019-03-23 20:37:18 +01:00
Ricki Hirner
8fe093d975 Create calendar: new UI, use ViewModel 2019-03-23 20:37:18 +01:00
Ricki Hirner
e99acb2921 Delete collection: use ViewModel 2019-03-23 20:37:18 +01:00
Ricki Hirner
88e0c16f83 AboutActivity: use ViewModel 2019-03-23 20:37:18 +01:00
Ricki Hirner
b348f54f95 Update beta feedback email address, libraries 2019-03-23 20:37:18 +01:00
Ricki Hirner
b75f8d140a Login: couple user name and email address 2019-03-23 20:37:18 +01:00
Ricki Hirner
206c2e8aa9 Account setup: fix crash 2019-03-23 20:37:18 +01:00
Ricki Hirner
e8f14c3745 Enable Multidex for >64k methods on Android 4.4 2019-03-23 20:37:18 +01:00
Ricki Hirner
c2e6514a3c AccountListFragment: use ViewModel 2019-03-23 20:37:18 +01:00
Ricki Hirner
676f74a528 HttpClient: close cache, provide app:html binding 2019-03-16 13:05:43 +01:00
Ricki Hirner
22b9624c0a Use ViewModel and data binding for login process 2019-03-16 13:02:10 +01:00
Ricki Hirner
f22dd3e013 Debug info: use ViewModel 2019-03-16 13:01:38 +01:00
Ricki Hirner
913b395e56 Update libraries; enable Java 8 compatibility 2019-03-16 13:01:26 +01:00
Ricki Hirner
f9974dbb7e Move CustomTlsSocketFactory to cert4android so it can be used in other apps like ICSx⁵; update ical4android 2019-02-20 18:29:43 +01:00
Ricki Hirner
49fae85852 Notify on invalid events, too 2019-02-19 21:24:09 +01:00
Ricki Hirner
3addab5bc0 Version bump to 2.3 2019-02-19 21:24:09 +01:00
Ricki Hirner
1bfacff3df Merge branch 'remove-stray-settings' into 'master-ose'
Remove stray Settings.kt file

See merge request bitfireAT/davx5-ose!18
2019-02-14 10:13:41 +00:00
Michael Biebl
643c4f04c9 Remove stray Settings.kt file
It was most likely added by accident in commit
7d4689969a
2019-02-14 01:40:54 +01:00
Ricki Hirner
517b60aa21 Metadata: allow translations over Transifex 2019-02-12 16:46:55 +01:00
Ricki Hirner
98eeaf171e Fetch translations from Transifex 2019-02-09 14:20:41 +01:00
Ricki Hirner
9320de54c3 Metadata: add feature graphic 2019-02-08 17:50:41 +01:00
Ricki Hirner
24730d1925 Notify on invalid iCalendar/vCard objects
* add notification channel: sync warnings
* notify on invalid iCalendar/vCard objects
* add app setting: Notification settings (only when notification channels are available)
2019-02-08 17:42:51 +01:00
Ricki Hirner
94fd665b6d Update Kotlin, gradle, dav4jvm 2019-02-07 13:21:25 +01:00
Ricki Hirner
7d9d9ea57c Translations: fix positional argument (thanks @mbiebl) 2019-02-06 17:28:33 +01:00
Ricki Hirner
bc1b1ca40f Update README, use absolute submodules URLs 2019-02-06 17:01:24 +01:00
Ricki Hirner
4ff8a5eb4e README update 2019-02-06 16:46:00 +01:00
Ricki Hirner
517a859dbf Fetch translations from Transifex 2019-02-06 16:45:38 +01:00
Ricki Hirner
1b6689133a Add Greek translation (thanks Efstathios Iosifidis) 2019-02-06 16:44:40 +01:00
Ricki Hirner
8a18e1e738 CalenderSyncManager/TaskSyncManager: log created/updated data; update
libs
2019-02-06 01:25:41 +01:00
Ricki Hirner
f241de264d Update ical4j; show new davx5 logger in scripts/adb-log.sh 2019-01-30 17:45:57 +01:00
Ricki Hirner
2cf73f0f07 Use AppCompatResources.getDrawable instead of context.getDrawable 2019-01-29 23:01:01 +01:00
Ricki Hirner
15008c1a53 Use HtmlCompat.fromHtml instead of deprected Html.fromHtml 2019-01-29 22:55:00 +01:00
Ricki Hirner
c90e491739 Update gradle, Kotlin 2019-01-29 20:02:08 +01:00
Ricki Hirner
1b5ea52138 Settings provider: more logging, scripts update, lib updates 2019-01-29 19:18:38 +01:00
Ricki Hirner
48478c421f Finish renaming dav4android → dav4jvm (thanks @mbiebl) 2019-01-27 12:15:52 +01:00
Ricki Hirner
f4823f9524 Version bump to 2.2.3.1 2019-01-18 16:16:01 +01:00
Ricki Hirner
2abe164e08 Don't use shouldShowRequestPermissionRationale in debug info (crashes on Motorola Android 6 when permission name is unknown) 2019-01-18 16:14:47 +01:00
Ricki Hirner
fa5856604d update homepage links; version bump to 2.2.3 2019-01-18 11:33:14 +01:00
Ricki Hirner
c88508ea66 Add Galician translation (thanks Xosé M. Lamas) 2019-01-18 09:58:55 +01:00
Ricki Hirner
e1c5fb2bfd Add Galician translation (thanks) 2019-01-18 09:58:32 +01:00
Ricki Hirner
9536d43047 Debug info: show location permission; show "Dont ask again" for permissions; show CalDAV "use event colors" option 2019-01-18 02:08:58 +01:00
Ricki Hirner
273c23ee44 Change some DAVdroid leftovers to DAVx⁵; remove Lombok config (thanks @mbiebl) 2019-01-18 01:43:33 +01:00
Ricki Hirner
a34b3b1fee AccountSettings version 9: disable OpenTasks isSyncable for non-CalDAV accounts 2019-01-18 01:33:30 +01:00
Ricki Hirner
6157d6e767 Disable OpenTasks sync on account creation without CalDAV service; Account UI: finish activity from main thread on account deletion 2019-01-18 01:33:24 +01:00
Ricki Hirner
013729ed73 Account settings: fix sync interval setting once more. Maybe it now works like this? 2019-01-17 12:27:51 +01:00
Ricki Hirner
2287b802ce Update README
* fix Twitter link (thanks Felix Eckhofer)
* update dav4jvm URL
2019-01-16 17:22:28 +01:00
Ricki Hirner
0166008ab6 Fetch translations from Transifex 2019-01-13 21:46:46 +01:00
Ricki Hirner
739bf3e53d Version bump to 2.2.3-beta1 2019-01-13 21:46:20 +01:00
Ricki Hirner
e36102721f Restrict sync to WiFi SSIDs: explain why Location permissions are required 2019-01-12 16:01:51 +01:00
Ricki Hirner
5ef9693ac8 Add some phone screenshots 2019-01-08 19:27:29 +01:00
Ricki Hirner
19320dda17 Version bump to 2.2.2 2019-01-08 19:19:43 +01:00
Ricki Hirner
322f9a0da0 Fetch translations from Transifex 2019-01-08 19:17:31 +01:00
Ricki Hirner
ba20d74b64 User-Agent and iCalendar/vCard product IDs: useBuild config; change to dav4jvm 2019-01-08 19:14:03 +01:00
Ricki Hirner
6a512e1f0f Fix crash on single-core devices and ProGuard rules 2019-01-08 01:21:37 +01:00
Ricki Hirner
4300954101 Fix CI in libs; include dav4jvm as standalone project 2019-01-06 20:01:29 +01:00
Ricki Hirner
2fe19e9342 Rename dav4android to dav4jvm; update gradle 2019-01-06 18:17:19 +01:00
Ricki Hirner
e764d31d18 Login: Show error message when account name is already taken 2019-01-05 11:55:32 +01:00
Ricki Hirner
8d0d920033 Version bump to 2.2.1 2019-01-04 22:30:41 +01:00
Ricki Hirner
e2fcbbea0b Fetch translations from Transifex 2019-01-04 22:30:41 +01:00
Ricki Hirner
1cefa88b98 Notifications: fix crashes (for Android 4.4 and when wrong CardDAV password occurs) 2019-01-04 22:13:47 +01:00
Ricki Hirner
be4a650deb Remove unnecessary permissions; ical4android update
* remove external storage permissions because logs are now written to the app data directory
* ical4android: use ical4j 2.2.3 with built-in RFC 7986 properties
2019-01-04 14:26:04 +01:00
Ricki Hirner
ab47b2b222 Debug info: fix onShare
* bump version code to 261
2019-01-04 01:04:40 +01:00
Ricki Hirner
7f301640da Account settings: reload in UI thread on Android callback
* version bump to 260
2019-01-03 19:20:52 +01:00
Ricki Hirner
83656ef497 Bump version code to 259 2019-01-03 18:55:09 +01:00
Ricki Hirner
77c54a2150 Fetch translations from Transifex 2019-01-03 18:54:52 +01:00
Ricki Hirner
16e3526a66 Retain sync intervals and isSyncable when renaming an account 2019-01-03 18:14:38 +01:00
Ricki Hirner
3ae5653da4 Install Webcal app: rename ICSdroid to ICSx⁵ 2019-01-03 17:13:40 +01:00
Ricki Hirner
c02b033f8c Version bump to 2.2 2019-01-03 16:53:24 +01:00
Ricki Hirner
4afe4837e7 Update strings and ical4j 2019-01-03 16:53:01 +01:00
Ricki Hirner
56813ad4d6 Logging: Use FileProvider instead of external file 2019-01-02 23:56:24 +01:00
Ricki Hirner
036fc405f4 Make sure the account is seen by OpenTasks 2019-01-02 22:07:25 +01:00
Ricki Hirner
95173dcd7d Ask for contacts/calendar permissions as soon there is a known CalDAV/CardDAV service 2019-01-02 22:07:25 +01:00
Ricki Hirner
be754d6ede Add F-Droid metadata 2018-12-31 11:26:34 +01:00
Ricki Hirner
20097c0b69 Version bump to 2.1 2018-12-30 20:34:36 +01:00
Ricki Hirner
0d1c265e97 Show Color Picker in README 2018-12-30 18:57:25 +01:00
Ricki Hirner
7ce739c271 Further DAVdroid -> DAVx5 replacements 2018-12-30 16:52:06 +01:00
Ricki Hirner
241957f2dd New launcher icons 2018-12-30 15:37:41 +01:00
Ricki Hirner
3dea33e100 Fix string references 2018-12-30 14:16:39 +01:00
Ricki Hirner
f8f60f4b52 Update CI script 2018-12-30 13:41:05 +01:00
Ricki Hirner
0d75cf0cdc Update strings 2018-12-30 12:37:54 +01:00
Ricki Hirner
556843df4f Remove "Accounts may be gone after rebooting" startup dialog 2018-12-30 12:08:45 +01:00
Ricki Hirner
8b12cb7a29 Fetch translations from Transifex 2018-12-30 11:59:27 +01:00
Ricki Hirner
07c5cdc754 Rename DAVdroid to DAVx⁵ 2018-12-30 11:16:23 +01:00
Ricki Hirner
1d33e4c311 Replace AmbilWarna by ColorPicker 2018-12-26 13:58:41 +01:00
Ricki Hirner
7d4689969a Refactor Settings provider
* don't use a separate :sync process anymore, so that settings management doesn't need IPC
* remove Settings service and IPC, use singleton with application Context instead
* adapt default number of sync worker threads
* library updates
2018-12-26 13:30:00 +01:00
Ricki Hirner
b863d355f6 Fetch translations from Transifex 2018-12-22 11:52:27 +01:00
Ricki Hirner
bcd468f2e5 DAV service detection: cancel previous notification (MR #15 thanks @mbiebl) 2018-12-22 11:52:27 +01:00
Ricki Hirner
21fdf2cebc Version bump to 2.0.7, fix small NPE 2018-12-22 11:52:27 +01:00
Ricki Hirner
ea071bbd1a Debug info: show details about DAVdroid, contact/calendar providers and contact/calendar/task apps 2018-12-21 18:03:36 +01:00
Ricki Hirner
bc5f1e935e Ignore HTTP 403 when the resource upload fails because of missing permissions (server will win) 2018-12-21 17:02:33 +01:00
Ricki Hirner
72749addcd Settings: add some icons; rearrange account settings 2018-12-08 23:25:19 +01:00
Ricki Hirner
6abbd019ad Version bump to 2.0.6 2018-12-05 12:56:51 +01:00
Ricki Hirner
67b1685d01 Version bump to 2.0.6-beta2 2018-12-03 11:19:01 +01:00
Ricki Hirner
4bbb2b8419 Ignore non-successful multiget responses 2018-12-01 22:01:37 +01:00
Ricki Hirner
8bdf03bfc5 Minor changes (lint/remove warnings) 2018-11-30 13:46:52 +01:00
Ricki Hirner
8b43677d01 Don't do emulator checks (because we can only use shared runners at the moment) 2018-11-30 11:40:24 +01:00
Ricki Hirner
e454fa398a Update ProGuard rules 2018-11-29 23:37:55 +01:00
Ricki Hirner
f5b1194599 Version bump to 2.0.6-beta1 2018-11-29 23:26:13 +01:00
Ricki Hirner
e549a25812 Update to okhttp 3.12.0 2018-11-29 23:25:43 +01:00
Ricki Hirner
fb90955b2a Switch to AndroidX 2018-11-29 23:03:38 +01:00
Ricki Hirner
a7f0161983 Merge branch 'dont-hardcode-account_type' into 'master-ose'
Don't hard-code accountType and use @string/account_type instead

See merge request bitfireAT/davdroid!12
2018-11-04 17:28:41 +00:00
Ricki Hirner
45efb88b1f Fetch translations from Transifex 2018-11-04 18:16:58 +01:00
Ricki Hirner
2c022d46fe Version bump to 2.0.5 2018-11-04 18:16:11 +01:00
Ricki Hirner
50c4ee94e2 Handle unresolvable ACTION_VIEW intents; update gradle, Kotlin 2018-11-04 18:15:07 +01:00
Michael Biebl
d06d24a13e Don't hard-code accountType and use @string/account_type instead 2018-10-18 19:57:05 +02:00
Ricki Hirner
2c5cdf813a Allow clear-text traffic (sync with http://) explicitly for Android 9 2018-10-07 18:53:39 +02:00
Ricki Hirner
0250baeeb3 Lint cleanup for Android P 2018-10-05 13:41:03 +02:00
Ricki Hirner
05d1970754 Version bump to 2.1-beta1 2018-10-05 13:21:04 +02:00
Ricki Hirner
a97be98b11 Mark as compatible with Android P (SDK level 28) 2018-10-05 13:21:04 +02:00
Ricki Hirner
1625f092fd Update Kotlin, gradle, build tools 2018-10-05 13:21:04 +02:00
Ricki Hirner
c0ff8f61f9 Resource detection: separate CalDAV/CardDAV detection; handle SocketTimeoutException better; version bump to 2.0.4 2018-09-08 21:49:21 +02:00
Ricki Hirner
dbfb74a16f Fetch translations from Transifex 2018-08-28 13:40:14 +02:00
Ricki Hirner
39b277bc6d Version bump to 2.0.3 2018-08-28 13:38:03 +02:00
Ricki Hirner
d09c70f52b More fine-grained WebDAV permissions
* service DB: split readOnly into privWriteContent and privUnbind
* collections: use privWriteContent (DAV:write-content privilege) for read-only detection
* AccountActivity: allow collection deletion only when privUnbind (DAV:unbind privilege) is set
2018-08-26 20:05:48 +02:00
Ricki Hirner
2f0ee8e230 Make debug info text selectable 2018-08-26 20:05:40 +02:00
Ricki Hirner
c320094b3b Service detection: ignore HTTP 4xx errors when looking for homesets 2018-08-25 15:59:05 +02:00
Ricki Hirner
5b12015307 Don't offer collection deletion for read-only collections 2018-08-25 15:59:05 +02:00
Ricki Hirner
e7d2c23989 Update Kotlin, dokka, build tools, FAQ URL, libs 2018-08-22 13:17:53 +02:00
Ricki Hirner
80971c52b5 Version bump to 2.0.2 2018-08-15 13:09:46 +02:00
Ricki Hirner
23abf7c1f0 DAVdroid address book sync: only sync address book sub-accounts of current account 2018-08-15 13:08:37 +02:00
Ricki Hirner
613b88ad5e Fix crash in resource detection 2018-08-15 12:43:22 +02:00
Ricki Hirner
b09d6c13b5 Fetch translations from Transifex 2018-08-10 21:25:43 +02:00
Ricki Hirner
e30ae04534 Version bump to 2.0.1 2018-08-10 21:25:01 +02:00
Ricki Hirner
0ed54bb671 Don't keep Acticity context when creating an account 2018-08-09 12:56:23 +02:00
Ricki Hirner
889ebe160e AccountActivity: use AsyncTask instead of own Executor 2018-08-07 19:20:36 +02:00
Ricki Hirner
602e14d2bf Really cancel resource detection when dialog is cancelled 2018-08-06 12:33:29 +02:00
Ricki Hirner
7ed275712e Use AsyncTask and progress indicator for creating an account 2018-08-06 12:33:25 +02:00
Ricki Hirner
3dc3e75721 Fetch translations from Transifex; always replace "..." by "…" 2018-08-05 10:55:15 +02:00
Ricki Hirner
a63e717ca7 OSE AboutActivity: take GPL from file and use loader 2018-08-05 10:47:58 +02:00
Ricki Hirner
c0044ae901 Don't use gradle variables in version name (F-Droid workaround) 2018-08-03 14:30:27 +02:00
Ricki Hirner
edabdbadc9 Collection properties: replace "Copy URL" button by selectable TextView 2018-08-03 14:30:04 +02:00
Ricki Hirner
47543e448c Version bump to 2.0 2018-07-30 10:16:19 +02:00
Ricki Hirner
5ff88c5b2b Fetch translations from Transifex 2018-07-28 14:33:53 +02:00
Ricki Hirner
eadfa39760 Version bump to 2.0-rc1 2018-07-28 14:33:05 +02:00
Ricki Hirner
2342bc92ca Vector drawable: fix Android 4.4 compatibility 2018-07-28 14:33:00 +02:00
Ricki Hirner
f0d150d491 Fetch translations from Transifex 2018-07-27 16:52:39 +02:00
Ricki Hirner
949a1916b5 Fix/improve error handling; bump version to 1.12-beta4 2018-07-27 16:49:57 +02:00
Ricki Hirner
efa4b7dc66 Version bump to 1.12-beta3 2018-07-22 11:24:14 +02:00
Ricki Hirner
971be44ffe Fetch translations from Transifex 2018-07-22 11:17:32 +02:00
Ricki Hirner
88efd5ab04 Use Apache Commons ContextedException instead of own class 2018-07-22 11:16:35 +02:00
Ricki Hirner
b44306eaf2 Rewrite startup strings 2018-07-21 11:08:12 +02:00
Ricki Hirner
abb0850889 Version bump to 1.12-beta2 2018-07-17 13:34:11 +02:00
Ricki Hirner
7760bddca7 Collection info: allow copying URL to clipboard 2018-07-17 13:24:57 +02:00
Ricki Hirner
60527e83fe Collection info fragment 2018-07-17 13:24:52 +02:00
Ricki Hirner
54cc9b39e1 Account activity: show "Select collections to synchronize" hint when no collections are selected 2018-07-16 20:05:40 +02:00
Ricki Hirner
be9d404e6c Update to okhttp 3.11 2018-07-16 14:01:38 +02:00
Ricki Hirner
fd40b5a4e3 Update build tools; enable parallel sync of address books authority 2018-07-15 17:10:57 +02:00
Ricki Hirner
abb142d118 Fetch translations from Transifex 2018-07-13 15:21:23 +02:00
Ricki Hirner
7a7a940089 New About activity
* new About activity using AboutLibraries library
* include DAVdroid version and other non-personal information in URL when DAVdroid homepage is opened
  (so that we know what DAVdroid versions are used out there and maybe can provide version-specific help)
2018-07-13 15:21:19 +02:00
Ricki Hirner
f0e1e03095 New sync logic with XML streaming
* refactored dav4android to use XML streaming
* refactored collection detection
* refactored sync logic
2018-07-12 00:05:53 +02:00
Ricki Hirner
2358996940 Enable strict mode for debugging; use thread for DB changes in AccountActivity 2018-06-27 12:55:07 +02:00
Ricki Hirner
09b42fc940 Change detection error message 2018-06-24 21:33:59 +02:00
Ricki Hirner
9c796f8226 Rename address book accounts correctly when renaming accounts; drop unparsable fields from vCards 2018-06-20 12:39:35 +02:00
Ricki Hirner
9a635875a1 Version bump to 1.11.5 2018-06-17 16:37:50 +02:00
Ricki Hirner
9d598cead7 move lambda expressions out of parentheses; use CREATOR-named companion objects for Parcelable 2018-06-17 16:37:11 +02:00
Ricki Hirner
2915a1f2b6 Fix lateinit null value in resource detection; update Kotlin and gradle 2018-06-16 15:08:06 +02:00
Ricki Hirner
5316fb42a5 Trigger a full calendar sync when past event time limit is changed in account settings 2018-06-14 09:21:42 +02:00
Ricki Hirner
1c3a5ceb09 Don't show contacts permission notification when no address book is selected for synchronization 2018-06-13 17:23:55 +02:00
Ricki Hirner
c34ca0f8e6 Version bump to 1.11.4.1 2018-06-12 11:15:40 +02:00
Ricki Hirner
b9f629f6ce Collection sync: don't reset "present remotely" flag after enumerating resources 2018-06-12 00:43:12 +02:00
Ricki Hirner
c6e37fcc58 Version bump to 1.11.4 2018-06-08 11:52:16 +02:00
Ricki Hirner
cb8c1f45ad Fetch translations from Transifex 2018-06-08 11:52:16 +02:00
Ricki Hirner
f7e1b97a66 Update gradle plugin 2018-06-08 11:52:16 +02:00
Ricki Hirner
90a70f39e8 Fix crash when collection is deleted 2018-05-30 10:33:26 +02:00
Ricki Hirner
9226d867c3 Update copyright 2018-05-28 12:04:56 +02:00
Ricki Hirner
216579680d Add maxOccurs to contacts.xml group membership (allows editing of contacts again) 2018-05-28 11:56:47 +02:00
Ricki Hirner
fe8b50c76d Fetch translations from Transifex 2018-05-28 10:47:33 +02:00
Ricki Hirner
9ab7b3b105 Add group memberships to contacts.xml so that they can be edited with some Contacts apps 2018-05-28 10:47:04 +02:00
Ricki Hirner
b90ec5b64c Version bump to 1.11.4-beta2 2018-05-27 16:28:41 +02:00
Ricki Hirner
b197ac7da2 Show HTTP request/response in debug info 2018-05-27 16:28:02 +02:00
Ricki Hirner
9aa58d170f Use US locale for date in User-Agent 2018-05-27 15:34:30 +02:00
Ricki Hirner
553b782339 Fix lateinit problem 2018-05-26 12:42:02 +02:00
Ricki Hirner
8b6376e01d Use HttpUrl whenever possible
* HttpUrl is the preferred class because we use URLs mainly for okhttp
* don't use URI or String for URLs, if possible
* HttpUrl is not Serializable, so use Parcelable for data classes with HttpUrl

X
2018-05-26 12:29:42 +02:00
Ricki Hirner
5da229ed88 Use weak reference for sync adapter thread lock 2018-05-26 12:29:38 +02:00
Ricki Hirner
a0bb681f48 Don't show InterruptedIOException; tests 2018-05-25 10:57:03 +02:00
Ricki Hirner
2e3987e6af Version bump to 1.11.4-beta1 2018-05-24 14:14:27 +02:00
Ricki Hirner
f72588a691 Update ical4android 2018-05-24 14:14:27 +02:00
Ricki Hirner
56fc2121f8 Integrate adaptive icon 2018-05-24 14:14:27 +02:00
Lokesh Krishna
c863d05f3f Adaptive icon 2018-05-24 14:14:27 +02:00
Ricki Hirner
f0ff12ebb9 dav4android update (immutable responses) 2018-05-24 14:14:27 +02:00
Ricki Hirner
87cb43b0e6 Fetch translations from Transifex 2018-05-24 14:14:27 +02:00
Ricki Hirner
2e073897b8 Collection sync: handle 403 with valid-sync-token precondition 2018-05-24 14:14:27 +02:00
Ricki Hirner
bdc032d1b9 Update build.gradle 2018-05-12 11:27:19 +02:00
Ricki Hirner
d07fcdcddc ical4android: reduce size of sent VTIMEZONEs 2018-05-11 23:18:54 +02:00
Ricki Hirner
5f2f151e14 Version bump to 1.11.3 2018-05-10 12:57:00 +02:00
Ricki Hirner
33ddb06272 Enable Collection Synchronization for CalDAV when past time event limit is disabled 2018-05-10 12:55:32 +02:00
Ricki Hirner
ef9f13d1d6 Version bump to 1.11.2 2018-05-04 11:01:06 +02:00
Ricki Hirner
6b9520287b Collection sync: mark skipped entries as locally present 2018-05-01 16:34:38 +02:00
Ricki Hirner
3944ab0222 Version bump to 1.11.2-beta1 2018-05-01 10:19:57 +02:00
Ricki Hirner
103459464c Improve collection sync for contacts 2018-04-30 12:42:15 +02:00
Ricki Hirner
be2d10278d Collection sync: don't download already available resources 2018-04-30 12:41:27 +02:00
Ricki Hirner
f4b864b3d0 Support collection sync (RFC 6578) for contacts 2018-04-30 12:41:21 +02:00
Ricki Hirner
a576701ee3 Code cleanup (lint) 2018-04-28 21:23:37 +02:00
Ricki Hirner
40bca54c09 Fix memory leak in vcard4android 2018-04-27 00:54:01 +02:00
Ricki Hirner
88efbd7a00 Add message to local storage error notification 2018-04-26 12:29:16 +02:00
Ricki Hirner
292eb3bb52 Fetch translations from Transifex 2018-04-26 10:53:53 +02:00
Ricki Hirner
bfddf92a3b Don't throw exception when content provider doesn't return all results; ignore RemoteException on getting contacts sync state 2018-04-26 10:53:27 +02:00
Ricki Hirner
872bfa2f40 Version bump to 1.11.1 2018-04-26 10:52:30 +02:00
Ricki Hirner
73d9dff1b7 Show and use ical4j and okhttp version numbers when possible 2018-04-25 01:04:13 +02:00
Ricki Hirner
fd84ff37e5 Log remote resource info for tasks, too 2018-04-19 08:30:23 +02:00
Ricki Hirner
d280ce9d63 Treat InterruptedIOException like IOException 2018-04-13 15:24:03 +02:00
Ricki Hirner
67c7be900a Fetch translations from Transifex 2018-04-13 10:24:33 +02:00
Ricki Hirner
c9cd6de476 Version bump to 1.11 2018-04-13 10:19:29 +02:00
Ricki Hirner
924ef0243d Fetch translations from Transifex 2018-03-29 14:41:22 +02:00
Ricki Hirner
62d9668081 Version bump to 1.11-rc1 2018-03-29 14:41:00 +02:00
Ricki Hirner
ab6fa7ecbf Update gradle, kotlin, build tools 2018-03-29 14:07:52 +02:00
Ricki Hirner
74b0e5cd31 Fetch translations from Transifex 2018-03-26 09:37:19 +02:00
Ricki Hirner
e2960ab572 Ask for LOCATION_COARSE permission for WiFi name detection on Android 8.1+ 2018-03-26 09:36:14 +02:00
Ricki Hirner
50bd119871 Don't re-schedule non-existent master events of deleted exceptions 2018-03-24 23:14:03 +01:00
Ricki Hirner
c1f3165f5d Improve OpenTasks install message 2018-03-20 12:26:13 +01:00
Ricki Hirner
915cd6ec44 Restrict more method names by interfaces 2018-03-16 13:16:17 +01:00
Ricki Hirner
73e936aeb5 Fix sync bugs 2018-03-15 19:37:04 +01:00
Ricki Hirner
190fc020ae Minor fixes 2018-03-15 18:14:28 +01:00
Ricki Hirner
c40a6bca2f OSE fixes 2018-03-15 17:37:19 +01:00
Ricki Hirner
3129fd909f Use Google repo 2018-03-15 13:11:00 +01:00
Ricki Hirner
31c77a1d57 Themeing, minor refactoring 2018-03-15 13:05:57 +01:00
Ricki Hirner
380562bd28 Add "app auto-start permission" startup dialog for specific vendors 2018-03-15 13:04:38 +01:00
Ricki Hirner
80b1cb55c7 Tests, minor refactoring 2018-03-15 13:01:41 +01:00
Ricki Hirner
59dd66383e Sync error notifications: retry action, lower importance of IOEXceptions 2018-03-15 13:01:19 +01:00
Ricki Hirner
44af04e310 Account activity: remove SnackBar message when a read-only address book is selected 2018-03-15 13:00:11 +01:00
Ricki Hirner
adf45cb569 Show startup fragments only once per AccountActivity lifecycle 2018-03-15 12:59:58 +01:00
Ricki Hirner
ab88020c15 Sync logic fix, theming, ProGuard 2018-03-15 12:58:56 +01:00
Ricki Hirner
e27c6fde08 Update to support library 27.1.0 and use it wherever possible
* Fragment transactions can now be done in onLoadFinished().
2018-03-15 12:57:51 +01:00
Ricki Hirner
3135af78af Tests 2018-03-15 12:44:43 +01:00
Ricki Hirner
9200ec89e1 Further improve notifications 2018-03-15 12:44:23 +01:00
Ricki Hirner
b37a2ad0e0 Remove Project Lombok from About
* not used anymore because of Kotlin
* thanks to Project Lombok!
2018-03-15 12:43:44 +01:00
Ricki Hirner
1df42350cc Remove PermissionsActivity
* asking for permissions is already (and better) done by AccountActivity
2018-03-15 12:43:32 +01:00
Ricki Hirner
3dbd5e3d18 Synchronization error messages / notifications 2018-03-15 12:41:53 +01:00
Ricki Hirner
7fdcb3710b Rewrite sync algorithm, prepare for WebDAV collection sync 2018-03-15 12:40:32 +01:00
Ricki Hirner
2f9f4f1d7b Optimize notification icons
* don't show DAVdroid icon unless there's a strong relationship to DAVdroid itself
2018-03-15 12:39:13 +01:00
Ricki Hirner
9fc3921b32 Account activity: show progress bar at beginning 2018-01-30 14:58:50 +01:00
Ricki Hirner
4f4a22a14e ical4android update 2018-01-27 22:57:34 +01:00
Ricki Hirner
16c44d8ad3 OpenTasks is installed by ical4android tests 2018-01-25 15:11:24 +01:00
Ricki Hirner
6cd2aa783e Prefer AutoCloseable over Closeable 2018-01-25 15:09:57 +01:00
Ricki Hirner
c2e161dac6 lib updates 2018-01-25 15:04:43 +01:00
Ricki Hirner
5a686a9a4c Version bump to 1.10.1.1 2018-01-20 17:11:58 +01:00
Ricki Hirner
7ba98d1c71 Hotfix: don't clear event colors 2018-01-20 17:08:29 +01:00
Ricki Hirner
fee5ab5064 Version bump to 1.10.1 2018-01-20 15:26:15 +01:00
Ricki Hirner
6e1b42e6a4 Fetch translations from Transifex 2018-01-20 15:23:32 +01:00
Ricki Hirner
c1ce953e3e ical4android: work around missing account separation when updating events
* fixes removed eventColor_index fields when there is at least one DAVdroid
  account with enabled calendar colors and at least one account with disabled
  calendar colors
2018-01-20 15:01:05 +01:00
Ricki Hirner
3a6db13df8 Update Kotlin 2018-01-18 16:17:29 +01:00
Ricki Hirner
d1d771b16a Version bump to 1.10 2018-01-18 16:11:54 +01:00
Ricki Hirner
f986d9807e Fetch translations from Transifex 2018-01-18 16:11:14 +01:00
Ricki Hirner
e3f33a5603 Test for client certificates 2018-01-18 16:10:26 +01:00
Ricki Hirner
702888e9bd Login activity: add padding; sync: re-throw Interrupted(IO)Exception 2018-01-15 21:33:00 +01:00
Ricki Hirner
6b41849cae Fetch translations from Transifex 2018-01-15 20:52:13 +01:00
Ricki Hirner
9dfb23b1d2 Remove unnecessary strings 2018-01-15 20:49:47 +01:00
Ricki Hirner
3468473f63 Improve sync error notifications
* refactor checking for cancelled sync
* notify on SSLHandshakeException (except when a certificate was rejected by cert4android)
* show exception cause in debug info
2018-01-15 20:43:07 +01:00
Ricki Hirner
bc63559a24 Login activity
* use TextInputLayout for input fields
* use support library instead of custom EditPassword widget
* improve client certificate UI
2018-01-15 14:00:03 +01:00
Ricki Hirner
3689df1385 Login with client certificates
* setup UI: login with URL and client certificate
* account settings UI: show either username/password or client certificate alias
* AccountSettings: serve credentials in generalized Credentials objects
* HttpClient: use Credentials (instead of username/password) for authentication
* HttpClient: always use CustomTlsSocketFactory
* CustomTlsSocketFactory: support client certificates
2018-01-13 22:56:13 +01:00
Ricki Hirner
dd3b326f66 Version bump to 1.9.10 2018-01-03 12:14:12 +01:00
Ricki Hirner
e2ca9eb7d7 dav4android update 2018-01-02 20:26:56 +01:00
Ricki Hirner
b001be1626 Fetch translations from Transifex 2018-01-02 19:41:53 +01:00
Ricki Hirner
e09714af32 Fix DB upgrade logic 2018-01-02 19:29:42 +01:00
Ricki Hirner
a4a816296b Refactoring 2018-01-01 15:13:57 +01:00
Ricki Hirner
65db706e06 Do contact provider settings only at address book creation (fixes changed contact visibility at sync) 2017-12-31 13:38:49 +01:00
Ricki Hirner
abd0eb533e Version bump to 1.9.9 2017-12-27 13:06:58 +01:00
Ricki Hirner
323cd48a30 Fetch translations from Transifex 2017-12-26 13:08:09 +01:00
Ricki Hirner
07b59fb6be Update for OpenTasks support
* ical4android update
* use uid field for tasks
* require OpenTasks 1.1.8.2
2017-12-26 13:08:04 +01:00
Ricki Hirner
3e2e17bdb2 Fix crash 2017-12-18 09:25:09 +01:00
Ricki Hirner
73c5c9ef64 Fix string resource 2017-12-16 22:09:21 +01:00
Ricki Hirner
12261a23ea Version bump to 1.9.8.1 2017-12-16 22:02:55 +01:00
Ricki Hirner
e19b045494 Fetch translations from Transifex 2017-12-16 22:01:35 +01:00
Ricki Hirner
076962e5ed Specify DNS server for dnsjava explicitly 2017-12-16 21:55:29 +01:00
Ricki Hirner
04306ed919 Version bump to 1.9.8 2017-12-16 18:56:16 +01:00
Ricki Hirner
93abef95f7 Fetch translations from Transifex 2017-12-16 18:54:16 +01:00
Ricki Hirner
54cc3bec9f Refactor Loaders, HttpClient
* improve Loader implementation
* use one shared HttpClient singleton
2017-12-16 18:47:48 +01:00
Ricki Hirner
d4324bcf8e Navigation drawer: add link to manual 2017-12-11 22:58:16 +01:00
Ricki Hirner
eff4438e9f Version bump to 1.9.7 2017-12-11 20:02:30 +01:00
Ricki Hirner
1f7298f947 Don't rely on LOGIN_ACCOUNTS_CHANGED_ACTION 2017-12-11 18:17:28 +01:00
Ricki Hirner
c823eb6efd Fetch translations from Transifex 2017-12-03 15:22:47 +01:00
Ricki Hirner
7e97017c41 Version bump to 1.9.6 2017-12-03 15:18:45 +01:00
Ricki Hirner
5e3b98a676 Target Android 8.1 (Oreo) 2017-11-30 12:58:08 +01:00
Ricki Hirner
8d60ee8d12 Define notification channels 2017-11-30 12:58:07 +01:00
Ricki Hirner
45af16afa6 Version bump to 1.9.5 2017-11-28 12:47:13 +01:00
Ricki Hirner
1b5bb875e0 Cert4android update 2017-11-28 12:46:59 +01:00
Ricki Hirner
09f946d5f4 Process colors for Webcal calendars, too 2017-11-27 17:54:49 +01:00
Ricki Hirner
0342242937 Provide title and color in Webcal intent; ical4android update 2017-11-27 17:25:57 +01:00
Ricki Hirner
46463bc2bc Fetch translations from Transifex 2017-11-25 18:48:51 +01:00
Ricki Hirner
3b13c0ef3c Version bump to 1.9.4 2017-11-25 18:48:14 +01:00
Ricki Hirner
a409273d8d Ask for permissions when account is opened (the permission notification is not always reliable) 2017-11-25 18:47:53 +01:00
Ricki Hirner
270607084e Upgrade libraries 2017-11-25 16:47:50 +01:00
Ricki Hirner
4be57313d7 New collection setting: "force read-only"
* collections in AccountActivity: replace long click by action overflow
2017-11-13 18:11:28 +01:00
Ricki Hirner
4e4cf7648b Version bump to 1.9.3 2017-11-12 17:34:41 +01:00
Ricki Hirner
5d084166f4 Fetch translations from Transifex 2017-11-11 21:25:39 +01:00
Ricki Hirner
173d744c2e Dokka KDoc 2017-11-11 21:18:31 +01:00
Ricki Hirner
d0e0b5d9e6 Raise API level, compatibility, make sync work again
* raise API level to 19 (required by ical4j)
* make HttpClient use Settings to get sync working again
* don't use vector graphics for notification icons (crashes on Android 4.x)
* various fixes and improvements
* library and build tools updates
2017-11-11 20:51:04 +01:00
Ricki Hirner
b2475d2b50 Refactoring
* refactor Settings and HttpClient
* library updates
2017-11-11 20:49:50 +01:00
Ricki Hirner
d0341ef1d0 cert4android: fetch translations 2017-11-04 12:00:19 +01:00
Ricki Hirner
34a55e68b0 Version bump to 1.9.2 2017-11-04 00:46:19 +01:00
Ricki Hirner
9a0cd64f68 SDK/build tools 27 2017-11-04 00:46:19 +01:00
Ricki Hirner
591a4ad423 another 5 minutes wasted life time 2017-10-31 14:01:42 +01:00
Ricki Hirner
7b479d270a Version bump 2017-10-31 13:41:29 +01:00
Ricki Hirner
58c44f6213 Fetch translations from Transifex 2017-10-31 13:38:38 +01:00
Ricki Hirner
b81a0c1eb6 Change tasks icon, add some view descriptions 2017-10-31 13:36:50 +01:00
Ricki Hirner
37a28df7d8 Handle exceptions in settings providers 2017-10-31 13:22:52 +01:00
Ricki Hirner
19796e6669 Fetch translations from Transifex 2017-10-31 09:19:50 +01:00
Ricki Hirner
87719f322b ical4android minor bug fix 2017-10-31 09:19:12 +01:00
Ricki Hirner
d40c2f5c56 lint 2017-10-29 14:57:40 +01:00
Ricki Hirner
48962d1bfa Use vector drawable support library for Android <5 2017-10-29 14:57:38 +01:00
Ricki Hirner
42ba48253f Update build tools, gradle, support library 2017-10-28 00:46:09 +02:00
Ricki Hirner
d55a1aa8b5 Version bump to 1.9.1 2017-10-25 20:43:00 +02:00
Ricki Hirner
dbc4ef9618 Fetch translations from Transifex 2017-10-25 20:37:40 +02:00
Ricki Hirner
3bf9be1443 Prevent multiple syncs to be run at the same time for the same account and authority 2017-10-25 18:40:02 +02:00
Ricki Hirner
e2b5ce7b10 Better handling of contact group method change
* ask for confirmation when group method is changed in account settings activity
* reset address books/force reload when contact group method has changed since last sync
2017-10-25 18:38:50 +02:00
Ricki Hirner
3d537b4472 Settings: add force reload 2017-10-25 15:22:32 +02:00
Ricki Hirner
64c3ca96ce Change homepage to davdroid.com 2017-10-23 14:23:41 +02:00
Ricki Hirner
71f73d66f2 Resource detection: use related replies to gather collection info, too 2017-10-20 13:56:38 +02:00
Ricki Hirner
0e4bb317f5 Version bump to 1.9-ose 2017-10-15 18:14:50 +02:00
Ricki Hirner
583280cf55 Change CI 2017-10-15 17:53:55 +02:00
Ricki Hirner
018f06fd53 Fetch translations from Transifex 2017-10-15 17:43:06 +02:00
Ricki Hirner
bf63605d50 Calendars: allow email reminders for events, too 2017-10-15 17:34:30 +02:00
Ricki Hirner
9643cb7661 StartupDialogFragment: Use activity instead of context 2017-10-13 21:37:00 +02:00
Ricki Hirner
46669dd0e9 README updates 2017-10-13 21:21:50 +02:00
Ricki Hirner
299f50f6d9 Fetch translations from Transifex 2017-10-13 20:43:58 +02:00
Ricki Hirner
af7f5e60bf Move to davdroid-ose repo 2017-10-13 14:55:38 +02:00
Ricki Hirner
d869ecbc0c Minor bug fixes, move beta feedback to navigation drawer 2017-10-13 13:41:14 +02:00
Ricki Hirner
d8536765e1 Managed DAVdroid, settings framework, theming, launcher icons, lib update 2017-10-13 12:54:43 +02:00
Ricki Hirner
9036f3e130 Assign UID/file name for empty contact even when creating in read-only address book (so that it can be deleted later) 2017-09-25 14:41:41 +02:00
Ricki Hirner
ab284c80df cert4android: fetch translations from Transifex 2017-09-25 12:08:37 +02:00
Ricki Hirner
89b324b299 Fetch translations from Transifex 2017-09-25 12:07:10 +02:00
Ricki Hirner
a5f86b13bd Version bump to 1.8.1 2017-09-25 12:03:46 +02:00
Ricki Hirner
3cdfc70692 Optimize read-only contact notifications 2017-09-25 12:03:04 +02:00
Ricki Hirner
29eb1fda9f Fetch translations from Transifex 2017-09-23 21:23:15 +02:00
Ricki Hirner
3c00520a09 Notify on read-only address books
* notify when a read-only address book is selected for synchronization
* notify when local changes have been discarded during sync
2017-09-23 21:12:09 +02:00
Ricki Hirner
d20aa562ad Initial implementation of read-only address books 2017-09-23 21:12:09 +02:00
Ricki Hirner
550612a282 Fetch translations from Transifex 2017-09-20 22:15:09 +02:00
Ricki Hirner
ef496f3986 Version bump to 1.8 2017-09-20 22:14:27 +02:00
Ricki Hirner
f4e7fd957d Reminders: use seconds, too 2017-09-20 22:13:45 +02:00
Ricki Hirner
4f6051a03b Fetch translations from Transifex 2017-09-15 13:29:02 +02:00
Ricki Hirner
670dc5ae9b Abbreviate too long log messages to reduce OOM errors 2017-09-15 13:26:03 +02:00
Ricki Hirner
7d6a052e74 Move strings out of global App context 2017-09-15 13:26:01 +02:00
Ricki Hirner
a7f59ded3f Change options for sync intervals
* remove 5 and 10 minutes because Android now enforces a minimum interval of 15 min
  (which makes sense in terms of battery optimization)
* add 30 min
2017-09-14 18:18:32 +02:00
Ricki Hirner
67646e4ce0 ical4j update 2017-09-14 17:29:58 +02:00
Ricki Hirner
d8346fa24a Fetch translations from Transifex 2017-09-13 17:20:51 +02:00
Ricki Hirner
54a41f02a4 Tests 2017-09-13 17:17:18 +02:00
Ricki Hirner
62d989d40a Webcal support in UI 2017-09-13 17:11:12 +02:00
Ricki Hirner
533abe80a1 database support for subscribed calendars (Webcal feeds)
* resource detection: detect/check subscribed calendars
* database: distinguish between CalDAV calendar and subscribed calendar
2017-09-13 17:09:01 +02:00
Ricki Hirner
4c8fa249cd Version bump to 1.7.3 2017-09-10 20:19:01 +02:00
Ricki Hirner
0551140f90 Fetch translations from Transifex 2017-09-10 20:17:10 +02:00
Ricki Hirner
a0bfe7a784 Make SyncManager Closeable 2017-09-10 20:16:08 +02:00
Ricki Hirner
45d6bf1eae Don't allow calendars which don't support VEVENT and/or VTODO to be selected for sync 2017-09-10 14:04:08 +02:00
Ricki Hirner
856f0bdf53 Upgrade to okhttp/3.9.0 2017-09-10 01:31:19 +02:00
Ricki Hirner
27831a0d4d cert4android: don't keep CustomCertManager in memory all the time
* remove static reference to CustomCertManager (and thus a Context)
* HttpClient now wraps OkHttpClient and is Closeable
2017-09-09 22:34:45 +02:00
Ricki Hirner
39fa8a1a14 Update cert4android 2017-09-09 22:25:42 +02:00
Ricki Hirner
13ca1a7ef9 Fetch cert4android translations from Transifex 2017-09-01 15:13:30 +02:00
Ricki Hirner
71804a59bb Fetch translations from Transifex 2017-09-01 15:12:16 +02:00
Ricki Hirner
37b3ee879d Version bump to 1.7.2 2017-09-01 15:11:46 +02:00
Ricki Hirner
57cb80768d donation popup; ProGuard
* don't show donation popup too often
* ProGuard fixes
2017-08-31 01:05:38 +02:00
Ricki Hirner
b6d378d346 Fetch cert4android translations from Transifex 2017-08-30 21:04:49 +02:00
Ricki Hirner
9ff53fbb1e Fetch translations from Transifex 2017-08-30 21:01:55 +02:00
Ricki Hirner
eac976379a Make event color support optional (opt-in) 2017-08-30 20:22:04 +02:00
Ricki Hirner
d444d95f12 Update to Android support library 26.0.1 2017-08-30 12:32:16 +02:00
Ricki Hirner
af61ac38c8 Optimize logging 2017-08-29 18:07:45 +02:00
Ricki Hirner
2e9f1d2d66 Fetch translations from Transifex 2017-08-22 15:16:54 +02:00
Ricki Hirner
2262685465 Version bump to 1.7.1 2017-08-22 15:16:04 +02:00
Ricki Hirner
4df37759d7 Add donation startup fragment again 2017-08-22 14:58:11 +02:00
Ricki Hirner
fd9e626593 ProGuard: keep ThreeTen; update build tools and Kotlin version 2017-08-22 14:44:38 +02:00
Ricki Hirner
9f890a8c8d Fetch translations from Transifex 2017-08-19 12:16:52 +02:00
Ricki Hirner
1e0a146dc4 Version bump to 1.7 2017-08-19 12:16:26 +02:00
Ricki Hirner
8836bf89a8 Fix migration 2017-08-15 16:11:38 +02:00
Ricki Hirner
dcbbc9a545 Fetch translations from Transifex 2017-08-15 15:52:23 +02:00
Ricki Hirner
319845f5b1 Update Kotlin, vcard4android 2017-08-15 15:51:55 +02:00
Ricki Hirner
dd7f1399f3 Handle conflict when renaming accounts 2017-08-10 17:39:39 +02:00
Ricki Hirner
8de6ebbe8f WiFi restriction: allow multiple SSIDs (comma-separated) 2017-08-08 13:28:04 +02:00
Ricki Hirner
82c5d424a7 Add support for event colors 2017-08-07 22:50:19 +02:00
Ricki Hirner
bdb15e0845 Scroll to external log settings when tapping notification 2017-08-05 15:50:50 +02:00
Ricki Hirner
14b5e66192 Version bump to 1.6.5 (160) 2017-08-05 13:30:02 +02:00
Ricki Hirner
636dd1b86a Fetch lib translations from Transifex 2017-08-05 13:15:10 +02:00
Ricki Hirner
152624a984 Fetch translations from Transifex 2017-08-05 13:13:25 +02:00
Ricki Hirner
3b9e95d4e3 Minor optimizations 2017-08-05 13:12:47 +02:00
Ricki Hirner
e64a5f408c Fix DavResourceFinder 2017-08-03 23:51:39 +02:00
Ricki Hirner
09e53e898d Fix device rotate crash bug, cert4android race condition 2017-08-03 20:47:28 +02:00
Ricki Hirner
d0f879820b Fix custom certificate socket factory 2017-08-02 17:58:57 +02:00
Ricki Hirner
02f9843d47 Remove TextUtils dependency; tests 2017-08-02 17:31:51 +02:00
Ricki Hirner
1c303e43eb Tests 2017-08-02 16:35:28 +02:00
Ricki Hirner
cfc7f96f83 Rewrite last UI classes to Kotlin; allow cancellation of resource detection 2017-08-02 16:28:59 +02:00
Ricki Hirner
47c1e0b8ea Rewrite most UI classes to Kotlin 2017-08-01 15:00:06 +02:00
Ricki Hirner
c5958de17b Fix addAccount authenticator crash 2017-07-31 17:48:18 +02:00
Ricki Hirner
b890c73d04 Rewrite some UI classes to Kotlin 2017-07-31 17:38:28 +02:00
Ricki Hirner
13b749974b Rewrite to Kotlin
* rewrite some UI classes
* move logic from App to CustomCertificates and Logger singletons
2017-07-31 15:55:01 +02:00
Ricki Hirner
f65ac7275e Allow to remove all WiFi SSID restrictions when "sync only on WiFi" is active 2017-07-30 14:55:49 +02:00
Ricki Hirner
0210ba0ad9 cert4android: fetch translations from Transifex 2017-07-27 16:51:08 +02:00
Ricki Hirner
d5a9a65209 Fetch translations from Transifex 2017-07-27 16:49:22 +02:00
Ricki Hirner
b16c7679b8 Version bump to 1.6.4 2017-07-27 16:48:06 +02:00
Ricki Hirner
c4ecc28d66 cert4android, vcard4android updates 2017-07-27 16:47:52 +02:00
Ricki Hirner
b638c2584b Improve debug info 2017-07-27 16:47:52 +02:00
Ricki Hirner
4be6a25eb6 Some minor fixes 2017-07-20 17:26:50 +02:00
Ricki Hirner
797bb78d20 Rewrite LoginCredentials to Kotlin 2017-07-19 22:05:58 +02:00
Ricki Hirner
ee696d233e Fix: SSLSocket doesn't implement Closeable in Android 4.1 2017-07-19 19:49:49 +02:00
Ricki Hirner
a1d2b2ab59 Update copyright 2017-07-19 19:32:46 +02:00
Ricki Hirner
4cb60ca78c Rewrite syncadapter package to Kotlin 2017-07-19 19:11:02 +02:00
Ricki Hirner
e2c47bbe92 Rewrite all remaining classes except syncadapter and ui package 2017-07-16 15:30:58 +02:00
Ricki Hirner
53e333ee22 Rewrite DavService to Kotlin 2017-07-16 01:07:58 +02:00
Ricki Hirner
a3181d35b4 Rewrite resource package to Kotlin 2017-07-14 20:58:29 +02:00
Ricki Hirner
4948471068 Rewrite some classes to Kotlin 2017-07-10 21:09:15 +02:00
Ricki Hirner
ed1acb47c7 Rewrite model package to Kotlin 2017-07-10 01:31:45 +02:00
Ricki Hirner
c73d47b1ec Rewrite log package to Kotlin 2017-07-08 14:31:23 +02:00
Ricki Hirner
b306933f95 Use x86 emulator 2017-07-07 00:14:05 +02:00
Ricki Hirner
dba55d2f2a Fetch translations from Transifex 2017-07-06 12:10:08 +02:00
Ricki Hirner
d20f0986d9 Version bump to 1.6.3 2017-07-06 12:07:27 +02:00
Ricki Hirner
1bc6880dc0 More logging (ical4j) + ProGuard 2017-07-06 12:06:54 +02:00
Ricki Hirner
8d0e4c6a46 Some fixes
* don't acquire contacts provider client for address book management (would require contacts permission check)
* rename task lists: handle all kinds of Exception
* lib updates
2017-07-04 15:01:06 +02:00
Ricki Hirner
1da8543e24 Minor bug/NPE fixes 2017-07-04 15:01:06 +02:00
Ricki Hirner
29792d915a Fetch translations from Transifex 2017-07-03 18:54:12 +02:00
Ricki Hirner
9654a23fd1 Require SDK level 16
In Android < 4.1 (< level 16), android.database.Cursor doesn't implement Closeable,
although it provides close(). Since we now use the Kotlin .use() idiom (which requires
a Closeable) instead of calling close() manually or via Lombok, we need Closeable
Cursors and it's not worth to work around this because Android 4.0.4 is now very old.
2017-07-03 18:46:56 +02:00
Ricki Hirner
4f8031a3a4 Improve preview release startup fragment 2017-07-02 14:42:01 +02:00
Ricki Hirner
eb6fa1ab7f Contacts: use hashCode() hack only for Android 7; problem has been fixed by Android 8 2017-07-02 14:08:04 +02:00
Ricki Hirner
29bf7ad204 vcard4android: Kotlin 2017-06-25 16:06:15 +02:00
Ricki Hirner
18e2a07415 Update gradle plugin 2017-06-21 13:00:08 +02:00
Ricki Hirner
49d1011b4a SDK/build tools level 26 2017-06-21 00:03:44 +02:00
Ricki Hirner
9b6c9b6c03 Version bump to 1.6.2 2017-06-20 15:18:16 +02:00
Ricki Hirner
953ffa6afe Update libs; update Commons Lang to 3.6 2017-06-20 15:17:30 +02:00
Ricki Hirner
e0a0f92c1e Lib updates, okhttp/3.8.1 2017-06-20 15:17:30 +02:00
Ricki Hirner
ce493865e7 ical4android: Kotlin 2017-06-20 15:17:30 +02:00
Ricki Hirner
77c9126be4 Version bump to 1.6.1.1 (151) 2017-06-16 18:57:06 +02:00
Ricki Hirner
9bb56d4f58 Lib updates 2017-06-16 17:54:41 +02:00
Ricki Hirner
908a3be678 Fetch translations from Transifex 2017-06-09 16:33:38 +02:00
Ricki Hirner
75dbbbf4ed Version bump to 1.6.1 2017-06-09 16:33:06 +02:00
Ricki Hirner
2599a15515 dav4android: Kotlin, cert4android: handle InterruptedException 2017-06-09 00:41:34 +02:00
Ricki Hirner
15fe102049 Birthdays without years, again 2017-06-05 14:35:54 +02:00
Ricki Hirner
6f55e51fef Fetch translations from Transifex 2017-06-03 19:27:49 +02:00
Ricki Hirner
1bdb004daf Version bump to 1.6 2017-06-03 19:27:06 +02:00
Ricki Hirner
30e83f45a5 Improve resource detection
* scan unrequested responses for useful auto-detection information
* cert4android: Kotlin
2017-06-03 19:26:43 +02:00
Ricki Hirner
7c8a8ee9fc Support for birthdays without year 2017-06-02 00:38:23 +02:00
Ricki Hirner
8747deeea7 Remove "ical4android" from iCal PRODID (same format as for VCard) 2017-05-14 13:36:49 +02:00
Ricki Hirner
63d2a7d0ef okhttp 3.8.0 ProGuard settings 2017-05-14 13:01:13 +02:00
Ricki Hirner
d89e0a392f Fetch translations from Transifex 2017-05-14 12:18:50 +02:00
Ricki Hirner
bb6ce6a355 Version bump to 1.5.2 2017-05-14 12:18:02 +02:00
Ricki Hirner
f59fd7205d Remove "vcard4android" from VCard PRODID to avoid folding for better compatibility 2017-05-14 12:11:58 +02:00
Ricki Hirner
0dd1fa47b8 Upgrade to okhttp/3.8.0 2017-05-14 11:33:42 +02:00
Ricki Hirner
a067ff03b3 Use UUIDs for newly generated event/task UIDs (RFC 7986 5.3 UID Property) 2017-04-27 13:36:10 +02:00
Ricki Hirner
a652a230c6 Fetch translations from Transifex 2017-04-25 14:11:47 +02:00
Ricki Hirner
c2a616fe52 Version bump to 1.5.1-ose 2017-04-25 14:11:22 +02:00
Ricki Hirner
1ac573d403 Use untranslated User-Agent string
* refactoring: don't use global string variables
2017-04-24 23:00:11 +02:00
Ricki Hirner
80a8ce6f8c Check sync conditions for contacts sync, too 2017-04-23 18:10:01 +02:00
Ricki Hirner
33e45d3ae9 Remove unnecessary InvalidAccountException 2017-04-22 21:37:19 +02:00
Ricki Hirner
f701a63e27 Allow null values for IS_ORGANIZER 2017-04-18 23:54:11 +02:00
Ricki Hirner
5594d2b284 README updates 2017-04-16 21:58:30 +02:00
Ricki Hirner
dcd99f7bca Version bump to 1.5.0.3 2017-04-16 15:51:51 +02:00
Ricki Hirner
df086e7ab5 Fetch translations from Transifex 2017-04-16 15:49:02 +02:00
Ricki Hirner
5b31d06dcf Upgrade libraries 2017-04-16 14:31:50 +02:00
Ricki Hirner
a39564a179 Unify strings
* HTTP client: use app name as User-Agent
* use string resources for homepage URLs
2017-04-11 16:35:29 +02:00
Ricki Hirner
18d8162074 gradle, dav4android updates
* update Android gradle plugin to 2.3.1
* dav4android update: follow redirects on DELETE
2017-04-08 20:26:38 +02:00
Ricki Hirner
27a4c47c35 Version bump to 1.5.0.2 2017-04-02 19:23:38 +02:00
Ricki Hirner
1bed502e67 Fix some inconsistencies 2017-04-02 19:19:38 +02:00
Ricki Hirner
d2dd27f99a Open DAVdroid main activity when add a "DAVdroid Address book" account is added over Settings 2017-03-30 21:23:29 +02:00
Ricki Hirner
2d83ed6be4 Account settings: restart loader after sync interval update
* debug info: add signature
2017-03-29 12:39:59 +02:00
Ricki Hirner
de608312a2 Hotfix: don't crash on empty address book displayName 2017-03-27 11:43:14 +02:00
Ricki Hirner
0b1bd35517 Version bump to 1.5 2017-03-26 19:30:16 +02:00
Ricki Hirner
44fb3713a4 Fetch translations from Transifex 2017-03-26 19:30:13 +02:00
Ricki Hirner
ce2e7e24b1 Remove all references to Constants.ACCOUNT_TYPE (now a to string resource) 2017-03-26 19:13:51 +02:00
Ricki Hirner
fa574aaa8d Improve address book details in debug info 2017-03-26 19:07:26 +02:00
Ricki Hirner
82aa7012d3 Add more debug information
* power saving status
* permissions
* address book accounts
2017-03-25 20:13:07 +01:00
Ricki Hirner
0789134948 Enable SSL_RSA_WITH_3DES_EDE_CBC_SHA for all Android versions
* refactor cipher selection
2017-03-25 20:13:07 +01:00
Ricki Hirner
d4bd96d846 Version bump to 1.5-beta1 2017-03-19 20:44:07 +01:00
Ricki Hirner
038b106b37 Multiple address books, segment 2
* migration from old address book to new address book accounts (5 -> 6)
* support renaming of address book accounts
* change address book accounts properly when the main account is renamed
2017-03-19 20:36:06 +01:00
Ricki Hirner
5a7aeb3eba Support for multiple address books per account
* new account type: "DAVdroid address book", which is assigned to a DAVdroid main account
* stub content provider "Address books", which enumerates all DAVdroid address book accounts and runs contacts sync for them
2017-03-19 20:28:13 +01:00
Ricki Hirner
a33e70162c Unify action bar icon colors 2017-03-12 13:47:52 +01:00
Ricki Hirner
3245b47635 Fix maven 2017-03-03 13:25:51 +01:00
Ricki Hirner
7b965769c9 Update translations from Transifex 2017-03-03 12:52:51 +01:00
Ricki Hirner
91f62614ab Update gradle 2017-03-03 12:51:12 +01:00
Ricki Hirner
c875b4b3ae Version bump to 1.4.1 2017-03-02 23:44:43 +01:00
Ricki Hirner
286ceeea3f Fetch translations from Transifex 2017-03-02 23:44:43 +01:00
Ricki Hirner
8b026d7e16 AccountsActivity: show message when global sync is disabled 2017-03-02 23:44:39 +01:00
Ricki Hirner
2f97830f28 Gradle update to 3.4 2017-02-27 17:39:22 +01:00
Ricki Hirner
75827d89c5 Fetch translations from Transifex 2017-02-17 13:55:08 +01:00
Ricki Hirner
1899c86494 Version bump to 1.4.0.3 2017-02-17 13:54:09 +01:00
Ricki Hirner
4a7b6cf546 Don't use uid2445 column on Android <4.2; alarm ACTION: compare only value (ignore parameters) 2017-02-17 09:55:50 +01:00
Ricki Hirner
0b83c1ad2f Version bump to 1.4.0.2 2017-02-12 19:01:43 +01:00
Ricki Hirner
a9380a8e1b Improve Android 7 workaround behavior in combination with CATEGORIES/VCard4 contact groups 2017-02-12 18:59:41 +01:00
Ricki Hirner
b756a5f54c Retain Events.UID_2445 when preparing events for upload
* move file name/UID generation from SyncManager to LocalContact, LocalEvent, LocalTask
* rename updateFileNameAndUID() to prepareForUpload()
* use random UUID for contacts, UidGenerator with Android device ID for events/tasks
* LocalEvent.prepareForUpload(): use existing UID_2445 if available
2017-02-10 18:06:28 +01:00
Ricki Hirner
762c379c54 Android 7 workaround: update hash after group membership operations 2017-02-10 18:06:28 +01:00
Ricki Hirner
8f8421e65e Version bump to 1.4.0.1 2017-02-06 11:59:06 +01:00
Ricki Hirner
0e67da5718 Android 7 workaround bugfix
* use local version of contact before calculating hash code
* don't stop upload sync if there are deleted contacts
2017-02-06 11:57:51 +01:00
Ricki Hirner
cf8b1e97bd Version bump to 1.4.0 2017-02-05 17:21:09 +01:00
Ricki Hirner
61885b41d9 Use contact hash codes only on Android 7+ (workaround)
vcard4android: don't hash CATEGORIES, more verbose logging
2017-02-05 17:20:49 +01:00
Ricki Hirner
f74cecdc1f Implement checksum to check whether DIRTY contacts have "really" changed
* contact data hash code = hash code of data fields and group memberships
* Before every contact sync, all dirty contacts are checked whether they're
  "really dirty" (= data hash code has changed). If they're not, the DIRTY
  flag is reset. Works around Android 7 behavior of setting contacts to DIRTY
  even if onky meta data has been updated (for instance, lastContacted after
  a call or SMS),
* When an "upload" sync is initiated by notifyChange and there are no
  "really dirty" contacts, the sync is ignored.
* contact upload: clearDirty() saves hash code, too
* contact download: create()/update() saves hash code, too
* debugging: sync flags (extras) are now logged
2017-02-01 19:19:17 +01:00
Ricki Hirner
d8ffd83e1e AccountSettingsActivity: use loader
* use Loader for AccountSettingsActivity sync intervals (fixes Android 7 display "issues")
* SyncManager: allow prepare() to skip synchronization
2017-02-01 01:20:44 +01:00
Ricki Hirner
666233136d Upgrade to okhttp 3.6.0 2017-01-30 22:27:54 +01:00
Ricki Hirner
eae5631d8a Version bump to 1.3.8 2017-01-29 20:11:15 +01:00
Ricki Hirner
dc614bf733 Fetch translations from Transifex (fixes crash in Spanish version) 2017-01-29 19:18:57 +01:00
Ricki Hirner
22c9603b3c Add information about current local and remote resource to debug info 2017-01-29 19:17:53 +01:00
Ricki Hirner
073f9230ba Upgrade to gradle 3.3 2017-01-29 19:17:53 +01:00
Ricki Hirner
3948e83f83 Use isAlwaysSyncable for contacts/calendars again because of buggy Android firmwares 2017-01-09 21:28:28 +01:00
Ricki Hirner
12664652e5 Version bump to 1.3.7 2017-01-08 23:35:41 +01:00
Ricki Hirner
d1162abf4c Change authentication restriction to domains instead of host names 2017-01-08 19:09:34 +01:00
Ricki Hirner
a93fa72d5a Delete local contacts when no CardDAV collection is selected 2017-01-06 15:53:23 +01:00
Ricki Hirner
3c8c3fe9b8 Version bump to 1.3.6 2017-01-01 12:43:47 +01:00
Ricki Hirner
a46f2d118c Fetch translations from Transifex 2017-01-01 12:26:02 +01:00
Ricki Hirner
dc0221dabb Update to ez-vcard 0.10.1
* fix REV and PREF problems
2017-01-01 01:13:03 +01:00
Ricki Hirner
a5e80d2268 Fix permissions notification
* ical4android: remove ORGANIZER from all VEVENT components if there are not attendees
2016-12-31 14:16:18 +01:00
Ricki Hirner
dbd200cb6c AccountSettings version 5: enable/disable OpenTasks by availability (Android 7.1.1 fix)
* better handling of setIsSyncable
2016-12-30 14:29:56 +01:00
Ricki Hirner
aacff632b7 Don't show warning on AccountSettings version updates 2016-12-30 14:28:28 +01:00
Ricki Hirner
e4861b0a68 Update to SDK level 25 2016-12-30 02:58:54 +01:00
Ricki Hirner
8d1b6da197 Change handling of tasks sync when OpenTasks is not installed
* AccountDetailsFragment: at account creation, enable task sync only when OpenTasks is installed
* PackageChangedReceiver: when packages are (un)installed, check for OpenTasks availability and (de)activate task sync for all accounts accordingly
* LocalTaskList: don't cache OpenTasks availability
* sync_*.xml: don't activate sync by default
2016-12-28 22:23:13 +01:00
Ricki Hirner
bf21c1338e Fetch translations from Transifex 2016-12-23 15:54:20 +01:00
Ricki Hirner
9452d6667a Version bump to 1.3.5 2016-12-23 15:51:56 +01:00
Ricki Hirner
6967773690 Address book selection changed: update URL as soon as possible 2016-12-19 18:58:35 +01:00
Ricki Hirner
73b95379fe Update gradle to 3.2.1; ical4android/vcard4android updates 2016-12-18 22:02:44 +01:00
Ricki Hirner
9f89173638 Update okhttp to 3.5.0 2016-12-02 15:01:52 +01:00
Ricki Hirner
77a0201327 Log group assignments more verbosely 2016-11-25 21:40:40 +01:00
Ricki Hirner
21c3f81a8b Rename account: don't crash when content providers are not accessible 2016-11-17 19:59:23 +01:00
Ricki Hirner
035dc4f0b2 Version bump to 1.3.4.1 2016-11-14 18:48:26 +01:00
Ricki Hirner
48fec240cf Fetch translations from Transifex 2016-11-14 18:41:48 +01:00
Ricki Hirner
82a6faa8de Avoid some crashes
* check whether ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATION can be resolved before launching it
* cert4android: don't crash when service can't be bound
2016-11-14 18:39:25 +01:00
Ricki Hirner
a4bd6d6a07 Allow renaming of accounts
* allow renaming of accounts
* always open AccountActivity, even if there are no services (so that users can delete the account from within DAVdroid)
2016-11-14 01:14:46 +01:00
Ricki Hirner
b91743239a Fetch translations from Transifex 2016-11-13 20:34:11 +01:00
Ricki Hirner
611dff15bb Update build tools to 25.0.0, fix WiFiManager leak 2016-11-13 20:22:10 +01:00
Ricki Hirner
cdd56d88fb Fetch translations from Transifex
ical4android: fix for events without dtend/duration
2016-11-06 17:36:59 +01:00
Ricki Hirner
198c53698d Fetch translations from Transifex 2016-11-04 12:02:08 +01:00
Ricki Hirner
3924b72a40 Version bump to 1.3.4
* library updates
2016-11-04 12:01:24 +01:00
Ricki Hirner
6520a4dff8 Add app-wide HTTP proxy setting 2016-10-30 22:21:54 +01:00
Ricki Hirner
9e62c44bef Debug info: send inline on Android <4.1 and when creating an attachment doesn't work 2016-10-22 02:05:58 +02:00
Ricki Hirner
9df01a7477 Version bump to 1.3.3.1 2016-10-21 19:52:55 +02:00
Ricki Hirner
c62eb6cd0e Fetch translations from Transifex 2016-10-21 19:50:09 +02:00
Ricki Hirner
8465df483e Library updates
* dav4android: disable compression for GET requests because it may change the ETag
* better logging for ical4j messages
* tests
2016-10-21 11:08:50 +02:00
Ricki Hirner
2366356922 ProGuard update; signing config 2016-10-18 12:36:41 +02:00
Ricki Hirner
5655aad59a Use string resource for logging file provider authority; vcard4android update 2016-10-17 23:57:53 +02:00
Ricki Hirner
65ddab6ed5 Share debug info: always use attachment
* share debug info: always use attachment (before: send inline if it was small enough)
* use FileProvider for debug info attachment (for Android 7 compatibility)
* dav4android, ical4android fixes
2016-10-17 17:47:27 +02:00
Ricki Hirner
d914addbb2 Add useless ProGuard rule 2016-10-15 16:51:35 +02:00
Ricki Hirner
b9a4489f3c Fetch translations from Transifex 2016-10-14 21:19:39 +02:00
Ricki Hirner
9d3b9c963a Version bump to 1.3.3 2016-10-14 21:00:29 +02:00
Ricki Hirner
72d981f361 dav4android: always use UTF-8 for Basic/Digest auth credentials 2016-10-13 15:23:11 +02:00
Ricki Hirner
6eeb484c56 ical4android: ignore invalid DUE < DTSTART for tasks 2016-10-12 17:03:46 +02:00
Ricki Hirner
cd7332b67c Remove VCard RFC6868 setting (always enabled now; setting not needed for Posteo compatibility anymore) 2016-10-12 16:45:26 +02:00
Ricki Hirner
1a8ff69750 Gitlab CI: install OpenTasks before tests 2016-10-12 13:04:21 +02:00
Ricki Hirner
991221f0ee vcard4android: ez-vcard 0.10.0 2016-10-11 23:36:30 +02:00
Ricki Hirner
ef4267e173 Test adaptions 2016-10-11 00:28:22 +02:00
Ricki Hirner
ba029e3da4 Switch to JUnit4 2016-10-10 21:03:18 +02:00
Ricki Hirner
4270807e2a Add Gitlab CI 2016-10-10 20:18:37 +02:00
Ricki Hirner
8456d078f1 Improve tests 2016-10-07 14:39:21 +02:00
Ricki Hirner
aa693cebb9 Fix NPE in "is refreshing progress bar" 2016-10-07 14:39:18 +02:00
Ricki Hirner
219a7ed38f Version bump to 1.3.2.2 2016-10-05 11:14:41 +02:00
Ricki Hirner
2aa90b4f9c Enable verbose logging of allow loggers (for instance, okhttp) / dav4android update 2016-10-04 23:42:03 +02:00
Ricki Hirner
26f9352411 Android 4.0/4.1 fixes
* require API level 15 for TransactionTooLargeException
* use SQLite WAL only on API level 16+
* various database access, provider access and UI fixes
2016-10-04 16:23:23 +02:00
Ricki Hirner
279c51c4a6 Version bump to 1.3.2 2016-10-03 20:57:14 +02:00
Ricki Hirner
98f4dd4c87 Fetch translations from Transifex 2016-10-03 20:43:01 +02:00
Ricki Hirner
5e1cb21d55 Avoid "no transaction" exception 2016-10-03 20:11:56 +02:00
Ricki Hirner
aa6e43cc94 Minimal layout change 2016-10-03 12:13:59 +02:00
Ricki Hirner
992e83fdb1 Show progress bar when synchronization is active 2016-09-26 23:07:35 +02:00
Ricki Hirner
cb73c1ebf7 Increase SEQUENCE only when we're ORGANIZER 2016-09-26 23:07:35 +02:00
Ricki Hirner
09d8f63515 Query/use CalDAV email address as account name, if available 2016-09-26 23:07:35 +02:00
Ricki Hirner
4f4cf25027 Always increase SEQUENCE 2016-09-26 23:07:35 +02:00
Ricki Hirner
de4e81977a lint: don't keep references to Context in static fields 2016-09-26 23:07:35 +02:00
Ricki Hirner
10bbfcabcb Version bump to 1.3.1
* some cert4android tests
2016-09-18 17:39:07 +02:00
Ricki Hirner
b5d295e57d Import strings from Transifex 2016-09-18 16:50:27 +02:00
Ricki Hirner
49555c34d8 Always use PROPFIND instead of REPORT addressbook-query 2016-09-18 16:43:11 +02:00
Ricki Hirner
9b6903572a README changes 2016-09-02 12:22:45 +02:00
Ricki Hirner
f16d64d636 Fetch translations from Transifex 2016-09-02 12:13:22 +02:00
Ricki Hirner
bceb5b643a lint optimizations
* permissions: declare AUTHENTICATE_ACCOUNTS, GET_ACCOUNTS and MANAGE_ACCOUNTS only until SDK level 22
* minor optimizations and bug fixes
2016-09-02 12:02:42 +02:00
Ricki Hirner
7e783feecf Version bump to 1.3
* vcard4android: fix bug concerning generated formatted postal addresses
2016-09-02 00:55:44 +02:00
Ricki Hirner
f9040f65fe New launcher logo 2016-09-02 00:55:39 +02:00
Ricki Hirner
b73a825ac9 Use cert4android instead of MemorizingTrustManager
* use cert4android instead of MemorizingTrustManager
* new app setting: distrust system certificates
* add network security config to manifest so that user-installed CAs will be accepted in Android 7 again
* update gradle
2016-09-02 00:38:02 +02:00
Ricki Hirner
67104b96fd Accept intent extras for LoginActivity 2016-08-13 23:14:33 +02:00
Ricki Hirner
e2c8fc9662 Fetch translations from Transifex 2016-08-06 00:13:16 +02:00
Ricki Hirner
40e46b721f Fix OpenTasks regression bug
* version bump to 1.2.3
* enable OpenTasks sync on Android <6 again
2016-08-05 23:32:03 +02:00
Ricki Hirner
30e6f44e44 Improve HTTP authentication
* use preemptive Basic auth automatically for HTTPS connections
* cache auth parameters (Basic/Digest)
2016-08-05 23:20:19 +02:00
Ricki Hirner
fd06c00fca Fetch translations from Transifex 2016-08-02 19:30:20 +02:00
Ricki Hirner
307c234f4a Request ignoring battery optimization
* startup dialog: request to ignore battery optimizations
* remove F-Droid donation startup dialog (only useful for davdroid-ose)
* version bump to 1.2.2
2016-08-02 19:30:15 +02:00
Ricki Hirner
d6dda6fbeb Avoid sync error when OpenTasks is not installed 2016-08-01 21:54:56 +02:00
Ricki Hirner
b4cb8234f1 Clean up launcher icon
* clean up launcher icon
* update dependencies
2016-08-01 21:15:55 +02:00
Ricki Hirner
a5fb9fd214 Allow large transactions
* version bump to 1.2.1-ose
* upgrade to okhttp 3.4.1
* ical4android/vcard4android: split oversized transactions
2016-07-27 14:33:06 +02:00
Ricki Hirner
3bbbba15b3 Remove gplay flavour to keep DAVdroid-OSE repo clean
* Remove gplay flavour to keep DAVdroid-OSE repo clean
* update Android gradle plugin to 2.1.2
2016-07-11 13:45:27 +02:00
729 changed files with 59046 additions and 19190 deletions

4
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
github: bitfireAT
liberapay: DAVx5
custom: 'https://www.davx5.com/donate'

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
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.

View File

@@ -0,0 +1,43 @@
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, …

View File

@@ -0,0 +1,19 @@
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 Normal file
View File

@@ -0,0 +1,38 @@
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 Normal file
View File

@@ -0,0 +1,20 @@
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 Normal file
View File

@@ -0,0 +1,58 @@
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 Normal file
View File

@@ -0,0 +1,55 @@
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 Normal file
View File

@@ -0,0 +1,48 @@
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 Normal file
View File

@@ -0,0 +1,81 @@
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
View File

@@ -8,8 +8,9 @@
# Files for the Dalvik VM
*.dex
# Java class files
# Java/Kotlin
*.class
.kotlin/
# Generated files
bin/

12
.gitmodules vendored
View File

@@ -1,12 +0,0 @@
[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 Normal file
View File

@@ -0,0 +1,30 @@
[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 Normal file
View File

@@ -0,0 +1,11 @@
# 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 Normal file
View File

@@ -0,0 +1,117 @@
**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).

46
README.md Normal file
View File

@@ -0,0 +1,46 @@
[![Website](https://img.shields.io/website?style=flat-square&up_color=%237cb342&url=https%3A%2F%2Fwww.davx5.com)](https://www.davx5.com/)
[![F-Droid](https://img.shields.io/f-droid/v/at.bitfire.davdroid?style=flat-square)](https://f-droid.org/packages/at.bitfire.davdroid/)
[![License](https://img.shields.io/github/license/bitfireAT/davx5-ose?style=flat-square)](https://github.com/bitfireAT/davx5-ose/blob/main/LICENSE)
[![Follow @davx5app@fosstodon.org](https://img.shields.io/mastodon/follow/109598783742737223?domain=https%3A%2F%2Ffosstodon.org&style=flat-square)](https://fosstodon.org/@davx5app)
[![Development tests](https://github.com/bitfireAT/davx5-ose/actions/workflows/test-dev.yml/badge.svg)](https://github.com/bitfireAT/davx5-ose/actions/workflows/test-dev.yml)
![DAVx⁵ logo](app/src/main/res/mipmap-xxxhdpi/ic_launcher.png)
DAVx⁵
========
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.
DAVx⁵ is licensed under the [GPLv3 License](LICENSE).
News and updates:
* [@davx5app@fosstodon.org](https://fosstodon.org/@davx5app) on Mastodon
**Help, feature requests, bug reports: [DAVx⁵ discussions](https://github.com/bitfireAT/davx5-ose/discussions)**
Parts of DAVx⁵ 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).**
USED THIRD-PARTY LIBRARIES
==========================
The most important libraries which are used by DAVx⁵ (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.

5
SECURITY.md Normal file
View File

@@ -0,0 +1,5 @@
# 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.

View File

@@ -1,9 +0,0 @@
flavor directory
gplay main + davdroid + gplay
icloud main + icloud
managed main + managed
soldupe main + soldupe
standard main + davdroid

View File

@@ -1,167 +0,0 @@
/*
* 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'
apply plugin: 'org.jetbrains.dokka-android'
ext {
baseVersionName = '2.0'
}
android {
compileSdkVersion 27
buildToolsVersion '28.0.1'
defaultConfig {
applicationId "at.bitfire.davdroid"
resValue "string", "packageID", applicationId
versionCode 241
buildConfigField "long", "buildTime", System.currentTimeMillis() + "L"
minSdkVersion 19 // Android 4.4
targetSdkVersion 27 // Android 8.1
buildConfigField "boolean", "customCerts", "false"
buildConfigField "boolean", "customCertsUI", "true"
// when using this, make sure that notification icons are real bitmaps
vectorDrawables.useSupportLibrary = true
}
flavorDimensions "type"
productFlavors {
standard {
dimension "type"
versionName baseVersionName
buildConfigField "boolean", "customCerts", "true"
}
managed {
dimension "type"
versionName "$baseVersionName-mgd"
applicationId "com.davdroid.managed"
resValue "string", "packageID", applicationId
buildConfigField "boolean", "customCerts", "true"
buildConfigField "boolean", "customCertsUI", "false"
minSdkVersion 21
}
gplay {
dimension "type"
versionName "$baseVersionName-gplay"
buildConfigField "boolean", "customCerts", "true"
}
icloud {
dimension "type"
versionName "$baseVersionName-icloud"
applicationId "at.bitfire.cloudsync"
resValue "string", "packageID", applicationId
}
soldupe {
dimension "type"
versionName "$baseVersionName-soldupe"
applicationId "com.soldupe.cloudsync"
resValue "string", "packageID", applicationId
minSdkVersion 21
}
}
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" ]
androidTest.java.srcDirs = [ "src/androidTest/java", "src/espressoTest/java" ]
}
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 'ImpliedQuantity', 'MissingQuantity' // quantities from Transifex may vary
disable 'MissingTranslation', 'ExtraTranslation' // translations from Transifex are not always up to date
disable "OnClick" // doesn't recognize Kotlin onClick methods
disable 'RtlEnabled'
disable 'RtlHardcoded'
disable 'Typos'
}
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/LICENSE'
}
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}
dependencies {
implementation project(':cert4android')
implementation project(':dav4android')
implementation project(':ical4android')
implementation project(':vcard4android')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:cardview-v7:27.1.1'
implementation 'com.android.support:design:27.1.1'
implementation 'com.android.support:preference-v14:27.1.1'
implementation 'com.github.yukuku:ambilwarna:2.0.1'
implementation 'com.mikepenz:aboutlibraries:6.0.9'
implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
implementation 'commons-io:commons-io:2.6'
implementation 'dnsjava:dnsjava:2.1.8'
implementation 'org.apache.commons:commons-lang3:3.7'
implementation 'org.apache.commons:commons-collections4:4.1'
// for tests
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
androidTestImplementation 'junit:junit:4.12'
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.11.0'
testImplementation 'junit:junit:4.12'
testImplementation 'com.squareup.okhttp3:mockwebserver:3.11.0'
}

219
app/build.gradle.kts Normal file
View File

@@ -0,0 +1,219 @@
/***************************************************************************************************
* 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)
}

View File

@@ -0,0 +1,60 @@
# 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

View File

@@ -1,44 +0,0 @@
# 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 javax.cache.** # no JCache support in Android
-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
-dontwarn org.conscrypt.**
# dnsjava
-dontwarn sun.net.spi.nameservice.** # not available on Android
# DAVdroid + libs
-keep class at.bitfire.** { *; } # all DAVdroid code is required

View File

@@ -0,0 +1,398 @@
{
"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')"
]
}
}

View File

@@ -0,0 +1,536 @@
{
"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')"
]
}
}

View File

@@ -0,0 +1,615 @@
{
"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')"
]
}
}

View File

@@ -0,0 +1,640 @@
{
"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')"
]
}
}

View File

@@ -0,0 +1,669 @@
{
"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')"
]
}
}

View File

@@ -0,0 +1,298 @@
{
"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')"
]
}
}

View File

@@ -0,0 +1,366 @@
{
"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')"
]
}
}

View File

@@ -0,0 +1,27 @@
<?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>

View File

@@ -1,128 +0,0 @@
/*
* 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 android.support.test.InstrumentationRegistry.getInstrumentation
import at.bitfire.cert4android.CustomCertManager
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.mockwebserver.MockWebServer
import org.apache.commons.io.IOUtils
import org.apache.commons.lang3.ArrayUtils.contains
import org.junit.After
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import java.net.Socket
import java.security.KeyFactory
import java.security.KeyStore
import java.security.Principal
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import java.security.spec.PKCS8EncodedKeySpec
import javax.net.ssl.SSLSocket
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509ExtendedKeyManager
import javax.net.ssl.X509TrustManager
class CustomTlsSocketFactoryTest {
private lateinit var certMgr: CustomCertManager
private lateinit var factory: CustomTlsSocketFactory
private val server = MockWebServer()
@Before
fun startServer() {
certMgr = CustomCertManager(getInstrumentation().context, false, true)
factory = CustomTlsSocketFactory(null, certMgr)
server.start()
}
@After
fun stopServer() {
server.shutdown()
certMgr.close()
}
@Test
fun testSendClientCertificate() {
var public: X509Certificate? = null
javaClass.classLoader.getResourceAsStream("sample.crt").use {
public = CertificateFactory.getInstance("X509").generateCertificate(it) as? X509Certificate
}
assertNotNull(public)
val keyFactory = KeyFactory.getInstance("RSA")
val private = keyFactory.generatePrivate(PKCS8EncodedKeySpec(readResource("sample.key")))
assertNotNull(private)
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
val alias = "sample"
keyStore.setKeyEntry(alias, private, null, arrayOf(public))
assertTrue(keyStore.containsAlias(alias))
val trustManagerFactory = TrustManagerFactory.getInstance("X509")
trustManagerFactory.init(null as KeyStore?)
val trustManager = trustManagerFactory.trustManagers.first() as X509TrustManager
val factory = CustomTlsSocketFactory(object: X509ExtendedKeyManager() {
override fun getServerAliases(p0: String?, p1: Array<out Principal>?): Array<String>? = null
override fun chooseServerAlias(p0: String?, p1: Array<out Principal>?, p2: Socket?) = null
override fun getClientAliases(p0: String?, p1: Array<out Principal>?) =
arrayOf(alias)
override fun chooseClientAlias(p0: Array<out String>?, p1: Array<out Principal>?, p2: Socket?) =
alias
override fun getCertificateChain(forAlias: String?) =
arrayOf(public).takeIf { forAlias == alias }
override fun getPrivateKey(forAlias: String?) =
private.takeIf { forAlias == alias }
}, trustManager)
/* known client cert test URLs (thanks!):
* - https://prod.idrix.eu/secure/
* - https://server.cryptomix.com/secure/
*/
val client = OkHttpClient.Builder()
.sslSocketFactory(factory, trustManager)
.build()
client.newCall(Request.Builder()
.get()
.url("https://prod.idrix.eu/secure/")
.build()).execute().use { response ->
assertTrue(response.isSuccessful)
assertTrue(response.body()!!.string().contains("CN=User Cert,O=Internet Widgits Pty Ltd,ST=Some-State,C=CA"))
}
}
@Test
fun testUpgradeTLS() {
val s = factory.createSocket(server.hostName, server.port)
assertTrue(s is SSLSocket)
val ssl = s as SSLSocket
assertFalse(contains(ssl.enabledProtocols, "SSLv3"))
assertTrue(contains(ssl.enabledProtocols, "TLSv1"))
assertTrue(contains(ssl.enabledProtocols, "TLSv1.1"))
assertTrue(contains(ssl.enabledProtocols, "TLSv1.2"))
}
private fun readResource(name: String): ByteArray {
this.javaClass.classLoader.getResourceAsStream(name).use {
return IOUtils.toByteArray(it)
}
}
}

View File

@@ -1,130 +0,0 @@
/*
* 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 android.support.test.filters.SmallTest
import at.bitfire.dav4android.DavResource
import at.bitfire.dav4android.property.ResourceType
import at.bitfire.davdroid.HttpClient
import at.bitfire.davdroid.model.ServiceDB.Collections
import okhttp3.HttpUrl
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.junit.After
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
class CollectionInfoTest {
private lateinit var httpClient: HttpClient
private val server = MockWebServer()
@Before
fun setUp() {
httpClient = HttpClient.Builder().build()
}
@After
fun shutDown() {
httpClient.close()
}
@Test
@SmallTest
fun testFromDavResource() {
// 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>"))
var info: CollectionInfo? = null
DavResource(httpClient.okHttpClient, server.url("/"))
.propfind(0, ResourceType.NAME) { response, _ ->
info = CollectionInfo(response)
}
assertEquals(CollectionInfo.Type.ADDRESS_BOOK, info?.type)
assertFalse(info!!.readOnly)
assertEquals("My Contacts", info?.displayName)
assertEquals("My Contacts Description", info?.description)
// 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>"))
info = null
DavResource(httpClient.okHttpClient, server.url("/"))
.propfind(0, ResourceType.NAME) { response, _ ->
info = CollectionInfo(response)
}
assertEquals(CollectionInfo.Type.CALENDAR, info?.type)
assertTrue(info!!.readOnly)
assertNull(info?.displayName)
assertEquals("My Calendar", info?.description)
assertEquals(0xFFFF0000.toInt(), info?.color)
assertEquals("tzdata", info?.timeZone)
assertTrue(info!!.supportsVEVENT)
assertTrue(info!!.supportsVTODO)
}
@Test
fun testFromDB() {
val values = 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)
val info = CollectionInfo(values)
assertEquals(CollectionInfo.Type.CALENDAR, info.type)
assertEquals(1.toLong(), info.id)
assertEquals(1.toLong(), info.serviceID)
assertEquals(HttpUrl.parse("http://example.com/"), info.url)
assertTrue(info.readOnly)
assertEquals("display name", info.displayName)
assertEquals("description", info.description)
assertEquals(0xFFFF0000.toInt(), info.color)
assertEquals("tzdata", info.timeZone)
assertTrue(info.supportsVEVENT)
assertTrue(info.supportsVTODO)
assertTrue(info.selected)
}
}

View File

@@ -1,39 +0,0 @@
/*
* 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))
}
}

View File

@@ -1,44 +0,0 @@
/*
* 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.support.test.InstrumentationRegistry
import android.support.test.InstrumentationRegistry.getTargetContext
import at.bitfire.davdroid.App
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
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))
}
}

View File

@@ -0,0 +1,20 @@
/***************************************************************************************************
* 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))
}
}

View File

@@ -0,0 +1,17 @@
/*
* 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)
}

View File

@@ -0,0 +1,123 @@
/*
* 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
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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()
}
}

View File

@@ -0,0 +1,38 @@
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>
}
}

View File

@@ -0,0 +1,82 @@
/*
* 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
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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()
}
}
}

View File

@@ -0,0 +1,156 @@
/*
* 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)
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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()
}

View File

@@ -0,0 +1,35 @@
/*
* 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)
}
}

View File

@@ -0,0 +1,122 @@
/*
* 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)
}
}

View File

@@ -1,30 +1,45 @@
/*
* 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
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
*/
package at.bitfire.davdroid
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() {
httpClient = HttpClient.Builder().build()
hiltRule.inject()
httpClient = HttpClient.Builder(context).build()
server = MockWebServer()
server.start(30000)
@@ -39,6 +54,7 @@ class HttpClientTest {
@Test
fun testCookies() {
Assume.assumeTrue(NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted)
val url = server.url("/test")
// set cookie for root path (/) and /test path in first response
@@ -61,7 +77,8 @@ class HttpClientTest {
httpClient.okHttpClient.newCall(Request.Builder()
.get().url(url)
.build()).execute()
assertEquals("cookie2=2; cookie1=1", server.takeRequest().getHeader("Cookie"))
val header = server.takeRequest().getHeader("Cookie")
assertTrue(header == "cookie1=1; cookie2=2" || header == "cookie2=2; cookie1=1")
server.enqueue(MockResponse()
.setResponseCode(200))

View File

@@ -0,0 +1,88 @@
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)
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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)
}
}

View File

@@ -0,0 +1,151 @@
/*
* 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()
}
}
}

View File

@@ -0,0 +1,151 @@
/*
* 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))
}
}
}

View File

@@ -0,0 +1,484 @@
/*
* 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))
}
}
}

View File

@@ -0,0 +1,281 @@
/*
* 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()
}
}

View File

@@ -0,0 +1,97 @@
/*
* 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))
}
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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())
}
}

View File

@@ -0,0 +1,97 @@
/*
* 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)
}
}
}

View File

@@ -0,0 +1,105 @@
/*
* 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())
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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])
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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)
}
}

View File

@@ -0,0 +1,718 @@
/*
* 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)
}
}
}

View File

@@ -1,32 +1,41 @@
/*
* 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
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
*/
package at.bitfire.davdroid.ui.setup
package at.bitfire.davdroid.servicedetection
import android.support.test.InstrumentationRegistry.getTargetContext
import android.support.test.filters.SmallTest
import at.bitfire.dav4android.DavResource
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.model.Credentials
import at.bitfire.davdroid.ui.setup.DavResourceFinder.Configuration.ServiceInfo
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.*
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 {
@@ -40,27 +49,47 @@ class DavResourceFinderTest {
private const val SUBPATH_ADDRESSBOOK = "/addressbooks/private-contacts"
}
val server = MockWebServer()
@get:Rule
val hiltRule = HiltAndroidRule(this)
lateinit var finder: DavResourceFinder
lateinit var client: HttpClient
lateinit var loginInfo: LoginInfo
@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 initServerAndClient() {
server.setDispatcher(TestDispatcher())
fun setup() {
hiltRule.inject()
server.dispatcher = TestDispatcher(logger)
server.start()
loginInfo = LoginInfo(URI.create("/"), Credentials("mock", "12345"))
finder = DavResourceFinder(getTargetContext(), loginInfo)
val baseURI = URI.create("/")
val credentials = Credentials("mock", "12345")
client = HttpClient.Builder()
.addAuthentication(null, loginInfo.credentials)
finder = resourceFinderFactory.create(baseURI, credentials)
client = HttpClient.Builder(context)
.addAuthentication(null, credentials)
.build()
Assume.assumeTrue(NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted)
}
@After
fun stopServer() {
fun teardown() {
server.shutdown()
}
@@ -72,7 +101,7 @@ class DavResourceFinderTest {
var info = ServiceInfo()
DavResource(client.okHttpClient, server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL))
.propfind(0, AddressbookHomeSet.NAME) { response, _ ->
finder.scanCardDavResponse(response, info)
finder.scanResponse(ResourceType.ADDRESSBOOK, response, info)
}
assertEquals(0, info.collections.size)
assertEquals(1, info.homeSets.size)
@@ -82,7 +111,7 @@ class DavResourceFinderTest {
info = ServiceInfo()
DavResource(client.okHttpClient, server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK))
.propfind(0, ResourceType.NAME) { response, _ ->
finder.scanCardDavResponse(response, info)
finder.scanResponse(ResourceType.ADDRESSBOOK, response, info)
}
assertEquals(1, info.collections.size)
assertEquals(server.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/"), info.collections.keys.first())
@@ -122,21 +151,33 @@ class DavResourceFinderTest {
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: Dispatcher() {
class TestDispatcher(
private val logger: Logger
): Dispatcher() {
override fun dispatch(rq: RecordedRequest): MockResponse {
if (!checkAuth(rq)) {
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 = rq.path
val path = request.path!!
if (rq.method.equals("OPTIONS", true)) {
if (request.method.equals("OPTIONS", true)) {
val dav = when {
path.startsWith(PATH_CALDAV) -> "calendar-access"
path.startsWith(PATH_CARDDAV) -> "addressbook"
@@ -147,7 +188,7 @@ class DavResourceFinderTest {
if (dav != null)
response.addHeader("DAV", dav)
return response
} else if (rq.method.equals("PROPFIND", true)) {
} else if (request.method.equals("PROPFIND", true)) {
val props: String?
when (path) {
PATH_CALDAV,
@@ -165,14 +206,21 @@ class DavResourceFinderTest {
" <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.log.info("Sending props: $props")
logger.info("Sending props: $props")
return MockResponse()
.setResponseCode(207)
.setBody("<multistatus xmlns='DAV:' xmlns:CARD='urn:ietf:params:xml:ns:carddav'>" +
.setBody("<multistatus xmlns='DAV:' xmlns:CARD='urn:ietf:params:xml:ns:carddav' xmlns:CAL='urn:ietf:params:xml:ns:caldav'>" +
"<response>" +
" <href>${rq.path}</href>" +
" <href>${request.path}</href>" +
" <propstat><prop>$props</prop></propstat>" +
"</response>" +
"</multistatus>")

View File

@@ -0,0 +1,93 @@
/*
* 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)
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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() {
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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()
}

View File

@@ -0,0 +1,192 @@
/*
* 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())
}
}
}

View File

@@ -0,0 +1,533 @@
/*
* 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
)
}

View File

@@ -0,0 +1,206 @@
/*
* 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) {}
}
}

View File

@@ -0,0 +1,127 @@
/*
* 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()
}

View File

@@ -0,0 +1,67 @@
/*
* 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))
}
}
}

View File

@@ -0,0 +1,152 @@
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)!!
}
}

View File

@@ -0,0 +1,96 @@
/***************************************************************************************************
* 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
}
}

View File

@@ -0,0 +1,103 @@
/***************************************************************************************************
* 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)
}
}
}

View File

@@ -0,0 +1,280 @@
/*
* 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())
}
}
}

View File

@@ -0,0 +1,99 @@
/*
* 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))
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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"))
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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))
}
}

View File

@@ -0,0 +1,254 @@
/*
* 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)
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* 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))
}
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2013 2015 Ricki Hirner (bitfire web engineering).
~ 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
@@ -9,6 +9,7 @@
<resources>
<string name="app_name">DavdroidTest</string>
<string name="app_name">Davx5Test</string>
<string name="account_type_test">at.bitfire.davdroid.test</string>
</resources>
</resources>

View File

@@ -0,0 +1,5 @@
<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" />

View File

@@ -1,24 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIEEzCCAfsCAQEwDQYJKoZIhvcNAQEFBQAwRjELMAkGA1UEBhMCQ0ExEzARBgNV
BAgMClNvbWUtU3RhdGUxEDAOBgNVBAoMB0NBIENlcnQxEDAOBgNVBAMMB0NBIENl
cnQwHhcNMTgwMTEzMjAyOTI5WhcNMTkwMTEzMjAyOTI5WjBZMQswCQYDVQQGEwJD
QTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMRIwEAYDVQQDDAlVc2VyIENlcnQwggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQDqOyHAeG4psE/f6i/eTfwbhn6j7WaFXxZiSOWwpQZmzRrx
MrfkABJCk0X7KNgCaJcmBkG9G1Ri4HfKrxvJFswMXknlq+0ulGBk7oDnZM+pihuX
3D9VCWMMkCqYhLCGADj2zB2mkX4LpcMRi6XoOetKURE/vcIy7rSLAtJM6ZRdftfh
2ZxnautS1Tyujh9Au3NI/+Of80tT/nA+oBJQeT1fB/ga1OQlZP5kjSaA7IPiIbTz
QBO+r898MvqK/lwsvOYnWAp7TY03z+vPfCs0zjijZEl9Wrl0hW6o5db5kU1v5bcr
p87hxFJsGD2HIr2y6kvYfL2hn+h9iANyYdRnUgapAgMBAAEwDQYJKoZIhvcNAQEF
BQADggIBAHANsiJITedXPyp89lVMEmGY3zKtOqgQ3tqjvjlNt2sdPnj7wmZbmrNd
sa90S/UwOn8PzEFOVxYy1BPlljlEjtjmc4OHMcm4P4Zv36uawHilmK8V+zT59gCK
ftB5FP2TLFUFi2X9o8J06d0xJRE77uewN155NV4RmPuP4b/tMmeixoQppHqLqEr5
lgEUnt3Mh1ctmeFQFJR6lJ01hlB0gdpVHIhzrVLTO3uo8ePLJTmxP6tyKl/HXj9F
mpVsKb1kriKwbkGczfw99OUZeUVbTwQOR07r0SrG71B7IuDvxIORnhQc1OUjt7ob
wjdaZauAHxpGBRu+hw9Yqaxchk9Gldy1nEjGyyVCD0FU5taXbl8PhBWEDc4U9tI+
xVNmPpsSuCsbz3Mjd1YIVRGL99vLrKsQcj+TNM+jJKKRKes3ihl+l/0FwG6UuO7L
EvjlUg5hOtYi1D7xuYyMjroGBGh7swYMt6w4eCDbcjzcCkaCi0H2pScM/rLBpDjS
LIoGCvZ1LBdi933/iOj1/8dxGZwY6fEgcyiD2n0xAgYIniLWjEZXOMdIK5FNTNga
Tswanvp+6Noa4oIu/hl/LXvPMsouaWfSEbRe0Dshi3GpLj3YtEHoN9DHB8bn7jy5
34By81GT41m5kq3hWP//x9kSHYSADpbovCbKbElU1qSt6vTVR4nq
-----END CERTIFICATE-----

View File

Binary file not shown.

View File

@@ -1,68 +0,0 @@
/*
* 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 {
companion object {
private const val BETA_FEEDBACK_URI = "mailto:support@davdroid.com?subject=${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME} feedback (${BuildConfig.VERSION_CODE})"
}
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 -> {
val intent = Intent(Intent.ACTION_SENDTO, Uri.parse(BETA_FEEDBACK_URI))
if (activity.packageManager.resolveActivity(intent, 0) != null)
activity.startActivity(intent)
}
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, App.homepageUrl(activity)))
R.id.nav_manual ->
activity.startActivity(Intent(Intent.ACTION_VIEW, App.homepageUrl(activity)
.buildUpon().appendEncodedPath("manual/").build()))
R.id.nav_faq ->
activity.startActivity(Intent(Intent.ACTION_VIEW, App.homepageUrl(activity)
.buildUpon().appendEncodedPath("faq/").build()))
R.id.nav_forums ->
activity.startActivity(Intent(Intent.ACTION_VIEW, App.homepageUrl(activity)
.buildUpon().appendEncodedPath("forums/").build()))
R.id.nav_donate ->
if (BuildConfig.FLAVOR != App.FLAVOR_GOOGLE_PLAY)
activity.startActivity(Intent(Intent.ACTION_VIEW, App.homepageUrl(activity)
.buildUpon().appendEncodedPath("donate/").build()))
else ->
return false
}
return true
}
}

View File

@@ -1,211 +0,0 @@
/*
* 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.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.security.KeyChain
import android.support.v4.app.Fragment
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_urlpwd.isChecked = true
v.urlpwd_base_url.setText(url)
v.urlpwd_user_name.setText(username)
v.urlpwd_password.setText(password)
} else {
v.login_type_email.isChecked = true
v.email_address.setText(username)
v.email_password.setText(password)
}
}
}
v.urlcert_select_cert.setOnClickListener {
KeyChain.choosePrivateKeyAlias(activity, { alias ->
Handler(Looper.getMainLooper()).post {
v.urlcert_cert_alias.text = alias
v.urlcert_cert_alias.error = null
}
}, null, null, null, -1, view!!.urlcert_cert_alias.text.toString())
}
v.login.setOnClickListener {
validateLoginData()?.let { info ->
DetectConfigurationFragment.newInstance(info).show(fragmentManager, null)
}
}
// initialize to Login by email
onCheckedChanged(v)
v.login_type_email.setOnCheckedChangeListener(this)
v.login_type_urlpwd.setOnCheckedChangeListener(this)
v.login_type_urlcert.setOnCheckedChangeListener(this)
return v
}
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
onCheckedChanged(view!!)
}
private fun onCheckedChanged(v: View) {
v.login_type_email_details.visibility = if (v.login_type_email.isChecked) View.VISIBLE else View.GONE
v.login_type_urlpwd_details.visibility = if (v.login_type_urlpwd.isChecked) View.VISIBLE else View.GONE
v.login_type_urlcert_details.visibility = if (v.login_type_urlcert.isChecked) View.VISIBLE else View.GONE
}
private fun validateLoginData(): LoginInfo? {
val view = requireNotNull(view)
when {
// Login with email address
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.text.toString()
if (password.isEmpty()) {
view.email_password.error = getString(R.string.login_password_required)
valid = false
}
return if (valid && uri != null)
LoginInfo(uri, email, password)
else
null
}
// Login with URL and user name
view.login_type_urlpwd.isChecked -> {
var valid = true
val baseUrl = Uri.parse(view.urlpwd_base_url.text.toString())
val uri = validateBaseUrl(baseUrl, false) { message ->
view.urlpwd_base_url.error = message
valid = false
}
val userName = view.urlpwd_user_name.text.toString()
if (userName.isBlank()) {
view.urlpwd_user_name.error = getString(R.string.login_user_name_required)
valid = false
}
val password = view.urlpwd_password.text.toString()
if (password.isEmpty()) {
view.urlpwd_password.error = getString(R.string.login_password_required)
valid = false
}
return if (valid && uri != null)
LoginInfo(uri, userName, password)
else
null
}
// Login with URL and client certificate
view.login_type_urlcert.isChecked -> {
var valid = true
val baseUrl = Uri.parse(view.urlcert_base_url.text.toString())
val uri = validateBaseUrl(baseUrl, true) { message ->
view.urlcert_base_url.error = message
valid = false
}
val alias = view.urlcert_cert_alias.text.toString()
if (alias.isEmpty()) {
view.urlcert_cert_alias.error = ""
valid = false
}
if (valid && uri != null)
return LoginInfo(uri, certificateAlias = alias)
}
}
return null
}
private fun validateBaseUrl(baseUrl: Uri, httpsRequired: Boolean, reportError: (String) -> Unit): URI? {
var uri: URI? = null
val scheme = baseUrl.scheme
if ((!httpsRequired && scheme.equals("http", true)) || scheme.equals("https", true)) {
var host = baseUrl.host
if (host.isNullOrBlank())
reportError(getString(R.string.login_url_host_name_required))
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) {
reportError(e.localizedMessage)
}
} else
reportError(getString(if (httpsRequired)
R.string.login_url_must_be_https
else
R.string.login_url_must_be_http_or_https))
return uri
}
class Factory: ILoginCredentialsFragment {
override fun getFragment() = DefaultLoginCredentialsFragment()
}
}

View File

@@ -1,201 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
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:checked="true"
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:paddingTop="8dp"
android:paddingBottom="16dp"
android:orientation="vertical">
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TextInputEditText
android:id="@+id/email_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/login_email_address"
android:inputType="textEmailAddress"/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:passwordToggleEnabled="true">
<android.support.design.widget.TextInputEditText
android:id="@+id/email_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/login_password"
android:fontFamily="monospace"
android:inputType="textPassword"/>
</android.support.design.widget.TextInputLayout>
</LinearLayout>
<RadioButton
android:id="@+id/login_type_urlpwd"
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_urlpwd_details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingBottom="16dp"
android:orientation="vertical">
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TextInputEditText
android:id="@+id/urlpwd_base_url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/login_base_url"
android:inputType="textUri"/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TextInputEditText
android:id="@+id/urlpwd_user_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/login_user_name"
android:inputType="textEmailAddress"/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:passwordToggleEnabled="true">
<android.support.design.widget.TextInputEditText
android:id="@+id/urlpwd_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:inputType="textPassword"
android:hint="@string/login_password"/>
</android.support.design.widget.TextInputLayout>
</LinearLayout>
<RadioButton
android:id="@+id/login_type_urlcert"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/login_type_url_certificate"
android:layout_marginTop="16dp"
android:paddingLeft="14dp" tools:ignore="RtlSymmetry"
style="@style/login_type_headline"/>
<LinearLayout
android:id="@+id/login_type_urlcert_details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingBottom="16dp"
android:orientation="vertical">
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TextInputEditText
android:id="@+id/urlcert_base_url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/login_base_url"
android:inputType="textUri"/>
</android.support.design.widget.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/urlcert_cert_alias"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:paddingLeft="3dp"
android:paddingRight="3dp"
style="@style/Base.TextAppearance.AppCompat.Body1"
android:textSize="16sp"/>
<Button
android:id="@+id/urlcert_select_cert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/Widget.AppCompat.Button.Borderless.Colored"
android:text="@string/login_select_certificate"/>
</LinearLayout>
</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>

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

View File

@@ -1,113 +0,0 @@
<?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 Llibreta dAdreces</string>
<string name="address_books_authority_title">Llibreta dadreces</string>
<string name="help">Ajuda</string>
<string name="manage_accounts">Gestiona comptes</string>
<string name="please_wait">Esperi si us plau...</string>
<string name="send">Enviar</string>
<string name="notification_channel_debugging">Depurador</string>
<string name="notification_channel_general">Altres missatges importants</string>
<string name="notification_channel_sync">Sincronització</string>
<string name="notification_channel_sync_io_errors">Xarxa i errors E/S</string>
<string name="notification_channel_sync_status">Estat dels misatges</string>
<!--startup dialogs-->
<string name="startup_battery_optimization_disable">Desactiva per DAVdroid</string>
<string name="startup_dont_show_again">No mostrar de nou</string>
<string name="startup_donate">Informació de Codi Obert</string>
<string name="startup_donate_message">Som feliços de que utilitzis DAVdroid, el qual és programari lliure (GPLv3). Desenvolupar DAVdroid ens porte moltes hores i molta feina, si us plau, considera fer una donació.</string>
<string name="startup_donate_now">Mostra la pàgina de donació</string>
<string name="startup_donate_later">Més tard</string>
<string name="startup_google_play_accounts_removed">Informació d\'errors DRM a Play Store</string>
<string name="startup_more_info">Més informació</string>
<string name="startup_opentasks_not_installed">OpenTasks no està instal·lada</string>
<string name="startup_opentasks_not_installed_install">Instal·lar OpenTasks</string>
<!--AboutActivity-->
<!--global settings-->
<string name="logging_no_external_storage">Emmagatzematge extern no trobat</string>
<!--AccountsActivity-->
<string name="navigation_drawer_about">Quan a / Llicència</string>
<string name="navigation_drawer_beta_feedback">Retroacció de la Beta</string>
<string name="navigation_drawer_settings">Paràmetres</string>
<string name="navigation_drawer_news_updates">Novetats &amp; Actualitzacions</string>
<string name="navigation_drawer_external_links">Enllaços externs</string>
<string name="navigation_drawer_website">Pàgina web</string>
<string name="navigation_drawer_manual">Manual</string>
<string name="navigation_drawer_faq">Preguntes Freqüents</string>
<string name="navigation_drawer_forums">Ajuda / Fòrums</string>
<string name="navigation_drawer_donate">Fer donació</string>
<string name="accounts_global_sync_enable">Activar</string>
<!--DavService-->
<!--AppSettingsActivity-->
<string name="app_settings">Paràmetres</string>
<string name="app_settings_user_interface">Interfície dusuari</string>
<string name="app_settings_reset_hints">Reiniciar pistes</string>
<string name="app_settings_security">Seguretat</string>
<string name="app_settings_debug">Depurador</string>
<!--AccountActivity-->
<string name="account_synchronize_now">Sincronitza ara</string>
<string name="account_synchronizing_now">Sincronitzant ara</string>
<string name="account_settings">Paràmetres del compte</string>
<string name="account_rename">Canvia el nom al compte</string>
<string name="account_rename_rename">Canvia el nom</string>
<string name="account_delete">Esborra el compte</string>
<string name="account_delete_confirmation_title">Segur que vols esborrar el compte?</string>
<string name="account_carddav">CardDAV</string>
<string name="account_caldav">CalDAV</string>
<string name="account_webcal">Webcal</string>
<string name="account_synchronize_this_collection">Sincronitzar aquesta col·lecció</string>
<string name="account_read_only">Només de lectura</string>
<string name="account_calendar">Calendari</string>
<string name="account_task_list">Llista de tasques</string>
<string name="account_refresh_address_book_list">Actualitza la llista de llibretes dadreces</string>
<string name="account_create_new_address_book">Crear nova llibreta dadreces</string>
<string name="account_refresh_calendar_list">Actualitzar llista de calendaris</string>
<string name="account_create_new_calendar">Crear nou calendari</string>
<string name="account_install_icsdroid">Instal·lar ICSdroid</string>
<!--AddAccountActivity-->
<string name="login_title">Afegir compte</string>
<string name="login_type_email">Entra amb una adreça de correu electrònic</string>
<string name="login_email_address">Correu electrònic</string>
<string name="login_email_address_error">Es requereix un correu electrònic vàlid</string>
<string name="login_password">Contrasenya</string>
<string name="login_password_required">Es requereix contrasenya</string>
<string name="login_type_url">Entra amb una URL i un nom d\'usuari</string>
<string name="login_url_must_be_http_or_https">La URL hauria de començar amb http(s)://</string>
<string name="login_url_must_be_https">La URL hauria de començar amb https://</string>
<string name="login_url_host_name_required">Es requereix nom d\'amfitrió</string>
<string name="login_user_name">Nom d\'usuari/a</string>
<string name="login_user_name_required">Es requereix nom d\'usuari/a</string>
<string name="login_base_url">URL base</string>
<string name="login_select_certificate">Selecciona certificat</string>
<string name="login_login">Entrada</string>
<string name="login_back">Sortir</string>
<string name="login_create_account">Crear compte</string>
<string name="login_account_name">Nom del compte</string>
<!--AccountSettingsActivity-->
<string name="settings_title">Paràmetres: %s</string>
<string name="settings_authentication">Autentificació</string>
<string name="settings_username">Nom d\'usuari/a</string>
<string name="settings_enter_username">Escriu el nom d\'usuari/a:</string>
<string name="settings_password">Contrasenya</string>
<string name="settings_enter_password">Escriu la teva contrasenya</string>
<string name="settings_sync">Sincronització</string>
<string name="settings_sync_summary_manually">Només a mà</string>
<string-array name="settings_sync_interval_names">
<item>Només a mà</item>
<item>Cada 15 minuts</item>
<item>Cada 30 minuts</item>
<item>Cada hora</item>
<item>Cada 2 hores</item>
<item>Cada 4 hores</item>
<item>Una vegada al dia</item>
</string-array>
<string name="settings_sync_wifi_only">Sincronitzar només amb WI-FI</string>
<string name="settings_carddav">CardDAV</string>
<string name="settings_caldav">CalDAV</string>
<!--collection management-->
<!--ExceptionInfoFragment-->
<!--sync adapters and DebugInfoActivity-->
<!--cert4android-->
</resources>

View File

@@ -1,179 +0,0 @@
<?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_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_opentasks_not_installed">OpenTasks není nainstalován</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_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 &amp; 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>
<!--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>
<!--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="many">Ignorovat události starší než %d dnů</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>
<!--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>

View File

@@ -1,238 +0,0 @@
<?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 adressebog</string>
<string name="address_books_authority_title">Adressebøger</string>
<string name="help">Hjælp</string>
<string name="manage_accounts">Administrere konti</string>
<string name="please_wait">Vent...</string>
<string name="send">Send</string>
<string name="notification_channel_debugging">Fejlsøgning</string>
<string name="notification_channel_general">Andre vigtige beskeder</string>
<string name="notification_channel_sync">Synkronisering</string>
<string name="notification_channel_sync_errors">Synkroniserings-fejl</string>
<string name="notification_channel_sync_io_errors">Netværks- og I/O-fejl</string>
<string name="notification_channel_sync_status">Status beskeder</string>
<!--startup dialogs-->
<string name="startup_battery_optimization_disable">Deaktivere 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_more_info">Mere information</string>
<string name="startup_opentasks_not_installed">OpenTasks ikke installeret</string>
<string name="startup_opentasks_not_installed_message">Du er nødt til at have den gratis app OpenTasks for at kunne synkronisere opgaver. (Ikke påkrævet for kontakter/begivenheder.)</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">Installere OpenTasks</string>
<!--AboutActivity-->
<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 fundet</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 synkroniseringsadapter</string>
<string name="navigation_drawer_about">Om / licens</string>
<string name="navigation_drawer_beta_feedback">Beta tilbagemelding</string>
<string name="navigation_drawer_settings">Opsætning</string>
<string name="navigation_drawer_news_updates">Nyheder &amp; opdateringer</string>
<string name="navigation_drawer_external_links">Eksterne henvisninger</string>
<string name="navigation_drawer_website">Netsted</string>
<string name="navigation_drawer_manual">Manual</string>
<string name="navigation_drawer_faq">OSS</string>
<string name="navigation_drawer_forums">Hjælp / fora</string>
<string name="navigation_drawer_donate">Donation</string>
<string name="account_list_empty">Velkommen til DAVdroid!\n\nDu kan nu tilføje en CalDAV/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">Aktivere</string>
<!--DavService-->
<string name="dav_service_refresh_failed">Registrering af tjeneste kunne ikke foretages</string>
<string name="dav_service_refresh_couldnt_refresh">Kunne ikke opdatere samling liste</string>
<!--AppSettingsActivity-->
<string name="app_settings">Indstillinger</string>
<string name="app_settings_user_interface">Brugerflade</string>
<string name="app_settings_reset_hints">Nulstil vejledende pop op</string>
<string name="app_settings_reset_hints_summary">Genaktivere tidligere lukket pop op</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">Fejlsøgning</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 fejlsøgnings information</string>
<string name="app_settings_show_debug_info_details">Vis/del software og opsætningsoplysninger</string>
<!--AccountActivity-->
<string name="account_synchronize_now">Synkronisere</string>
<string name="account_synchronizing_now">Synkroniserer</string>
<string name="account_settings">Opsætning af konti</string>
<string name="account_rename">Omdøbe 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øbe</string>
<string name="account_delete">Slette konto</string>
<string name="account_delete_confirmation_title">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_carddav">CardDAV</string>
<string name="account_caldav">CalDAV</string>
<string name="account_webcal">Webcal</string>
<string name="account_synchronize_this_collection">synkronisere samling</string>
<string name="account_read_only">skrivebeskyttet</string>
<string name="account_calendar">kalender</string>
<string name="account_task_list">opgave liste</string>
<string name="account_refresh_address_book_list">Opdatere adressebogslister</string>
<string name="account_create_new_address_book">Oprette ny adressebog</string>
<string name="account_refresh_calendar_list">Opdatere kalenderliste</string>
<string name="account_create_new_calendar">Oprette ny kalender</string>
<string name="account_no_webcal_handler_found">Der er ikke fundet noget program der kan håndtere Webcal.</string>
<string name="account_install_icsdroid">Installere ICSdroid</string>
<!--AddAccountActivity-->
<string name="login_title">Tilføje konto</string>
<string name="login_type_email">Logge ind med e-post adresse</string>
<string name="login_email_address">E-post adresse</string>
<string name="login_email_address_error">Gyldig e-post adresse 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">Logge ind med URL og brugernavn</string>
<string name="login_url_must_be_http_or_https">URL skal starte med http(s)://</string>
<string name="login_url_must_be_https">URL skal starte med https://</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_type_url_certificate">Logge ind med URL og klientcertifikat</string>
<string name="login_select_certificate">Vælge certifikat</string>
<string name="login_login">Logge ind</string>
<string name="login_back">Tilbage</string>
<string name="login_create_account">Oprette 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 serveren...</string>
<string name="login_no_caldav_carddav">Kunne ikke finde CalDAV- eller CardDAV-tjeneste.</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">Indtaste 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_certificate_alias">Alias for klientcertifikat</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-array name="settings_sync_interval_names">
<item>Kun manuelt</item>
<item>Hvert 15. minut</item>
<item>Hver halve time</item>
<item>Hver time</item>
<item>Hver 2. time</item>
<item>Hver 4. time</item>
<item>En gang om dagen</item>
</string-array>
<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_sync_wifi_only_ssids">WiFi SSID-begrænsning</string>
<string name="settings_sync_wifi_only_ssids_on">Synkroniserer kun over %s</string>
<string name="settings_sync_wifi_only_ssids_off">Alle WiFi-forbindelser vil blive anvendt</string>
<string name="settings_sync_wifi_only_ssids_message">Kommaseparerede navne (SSID\'er) over tilladte WiFi-netværk (efterlad blank for at bruge alle)</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_contact_group_method_change">Skift grupperingsmetode</string>
<string name="settings_contact_group_method_change_reload_contacts">Dette kræver, at alle kontakter genindlæses. Lokale ændringer, der ikke er gemt, vil blive slettet.</string>
<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>
<string name="settings_event_colors">Farver for begivenheder</string>
<string name="settings_event_colors_on">Synkroniser farver for begivenheder</string>
<string name="settings_event_colors_off">Synkroniser ikke farver for begivenheder</string>
<string name="settings_event_colors_off_confirm">Fjernes farver for begivenheder kan det fjerne farver, der allerede er synkroniseret.</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>
<string name="collection_force_read_only">Sæt skrivebeskyttet</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_contacts_read_only_address_book">Skrivebeskyttet adressebog</string>
<plurals name="sync_contacts_local_contact_changes_discarded">
<item quantity="one">Lokal ændring slettet</item>
<item quantity="other">%d lokale ændringer slettet</item>
</plurals>
<string name="sync_error_permissions">DAVdroid-rettigheder</string>
<string name="sync_error_permissions_text">Yderligere adgang påkrævet</string>
<string name="sync_error_opentasks_too_old">OpenTasks-version for gammel</string>
<string name="sync_error_opentasks_required_version">Påkrævet version: %1$s (aktuelt%2$s)</string>
<string name="sync_error_authentication_failed">Login mislykkedes (check loginoplysninger)</string>
<string name="sync_error_io">Netværks- eller I/O-fejl - %s</string>
<string name="sync_error_http_dav">HTTP-serverfejl - %s</string>
<string name="sync_error_local_storage">Lokal lagringsfejl - %s</string>
<string name="sync_error_retry">Gentag</string>
<string name="sync_error_view_item">Vis element</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>

View File

@@ -1,229 +0,0 @@
<?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">Agenda DAVdroid</string>
<string name="address_books_authority_title">Agendas</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>
<string name="notification_channel_debugging">Depuración</string>
<string name="notification_channel_general">Otros mensajes importantes</string>
<string name="notification_channel_sync">Sincronización</string>
<string name="notification_channel_sync_errors">Errores de sincronización</string>
<string name="notification_channel_sync_io_errors">Errores de Red y E/S</string>
<string name="notification_channel_sync_status">Mensajes de estado</string>
<!--startup dialogs-->
<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_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">Para sincronizar tareas, la aplicación libre OpenTasks es requerida. (No necesaria para contactos/eventos.)</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_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_beta_feedback">Retroalimentación Beta</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_manual">Manual</string>
<string name="navigation_drawer_faq">Preguntas frequentes</string>
<string name="navigation_drawer_forums">Ayuda / Foros</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>
<string name="accounts_global_sync_disabled">Sincronización automática del sistema completo está deshabilitada</string>
<string name="accounts_global_sync_enable">Activar</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_carddav">CardDAV</string>
<string name="account_caldav">CalDAV</string>
<string name="account_webcal">Webcal</string>
<string name="account_synchronize_this_collection">sincronizar ésta colección</string>
<string name="account_read_only">solo lectura</string>
<string name="account_calendar">calendario</string>
<string name="account_task_list">lista de tareas</string>
<string name="account_refresh_address_book_list">Refrescar contactos</string>
<string name="account_create_new_address_book">Crear nueva agenda</string>
<string name="account_refresh_calendar_list">Refrescar calendario</string>
<string name="account_create_new_calendar">Crear nuevo calendario</string>
<string name="account_no_webcal_handler_found">No se encontró aplicación para administrar Webcal</string>
<string name="account_install_icsdroid">Instale ICSdroid</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_must_be_https">El URL debe comenzar con https://</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_type_url_certificate">Iniciar sesión con URL y certificado del cliente</string>
<string name="login_select_certificate">Seleccionar un certificado</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>
<!--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_certificate_alias">Alias del certificado cliente</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_sync_wifi_only_ssids">Restricción WiFi SSID</string>
<string name="settings_sync_wifi_only_ssids_on">Solo se sincronizará a través de %s</string>
<string name="settings_sync_wifi_only_ssids_off">Todas las conexiones WiFi serán usadas</string>
<string name="settings_sync_wifi_only_ssids_message">Nombres separados por comas (SSIDs) de redes WiFi permitidas (deje vacío para todas)</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_contact_group_method_change">Cambie método de grupo</string>
<string name="settings_contact_group_method_change_reload_contacts">Esto requiere recargar todos los contactos. Cambios locales sin guardar serán descartados.</string>
<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>
<string name="settings_event_colors">Soporte de colores en eventos</string>
<string name="settings_event_colors_on">Sincronizar colores de eventos</string>
<string name="settings_event_colors_off">No sincronizar colores de eventos</string>
<string name="settings_event_colors_off_confirm">Desactivar colores de eventos podría remover colores de eventos ya sincronizados.</string>
<!--collection management-->
<string name="create_addressbook">Crear nueva agenda</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>
<string name="collection_force_read_only">Forzar solo-lectura</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_contacts_read_only_address_book">Libro de direcciones solo-lectura</string>
<plurals name="sync_contacts_local_contact_changes_discarded">
<item quantity="one">Cambio de contacto local descartado</item>
<item quantity="other">%d cambios de contactos locales descartados</item>
</plurals>
<string name="sync_error_permissions">Permisos de DAVdroid</string>
<string name="sync_error_permissions_text">Permisos adicionales requeridos</string>
<string name="sync_error_opentasks_too_old">OpenTasks muy viejo</string>
<string name="sync_error_opentasks_required_version">Versión requerida: %1$s (actual %2$s)</string>
<string name="sync_error_authentication_failed">Falló la autenticación (revise credenciales de inicio de sesión)</string>
<string name="sync_error_io">Error de red o E/S %s</string>
<string name="sync_error_http_dav">Error de servidor %s</string>
<string name="sync_error_local_storage">Error de almacenamiento local %s</string>
<string name="sync_error_retry">Reintentar</string>
<string name="sync_error_view_item">Ver item</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>

View File

@@ -1,206 +0,0 @@
<?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_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_opentasks_not_installed">L\'application OpenTasks n\'est pas installée</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_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 &amp; 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_calendar">calendrier</string>
<string name="account_task_list">liste de tâche</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>
<!--AddAccountActivity-->
<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>
<!--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">Restriction 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 dun 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>
<!--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>

Some files were not shown because too many files have changed in this diff Show More