Compare commits

..
1665 Commits
Author SHA1 Message Date
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
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
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
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
Ricki Hirner ab72b0df1d Fix NPE, add intent to view FAQ to upgrade notification 2016-07-11 12:55:55 +02:00
Ricki Hirner 909051521e Version bump to 1.2
* move ETag requirement from vcard4android to davdroid
* more debug info
* vcard4android: support for custom labels (X-ABLabel)
2016-07-02 10:32:12 +02:00
Ricki Hirner b9f07386c1 Support X-ABLabel for custom types
* vcard4android: support X-ABLabel for custom types
2016-07-01 22:10:20 +02:00
Ricki Hirner ddca7992ff Version bump to 1.1.1.2
* allow ProGuard optimization to remove non-relevant bytecode from flavors
* check flavors with direct comparison instead of .equals() to allwo optimizations
* store cookies per HttpClient, and not per DAVdroid instance (allows multiple sessions for parallel syncs)
* fetch translations from Transifex
2016-06-24 13:39:26 +02:00
Ricki Hirner 216c1dee80 Add standard and gplay product flavor 2016-06-24 00:06:43 +02:00
Ricki Hirner 27cf6ab222 Version bump to 1.1.1.1
* add yield points to allow processing of groups with many contacts
* new script to generate contacts for testing
2016-06-23 11:42:12 +02:00
Ricki Hirner 70ce90b40b Version bump to 1.1.1
* fetch translations from Transifex
2016-06-21 21:05:18 +02:00
Ricki Hirner 0feacb5daa Handle cookies correctly using a name/domain/path MultiKeyMap 2016-06-21 20:51:52 +02:00
Ricki Hirner 230e64ecac Version bump to 1.1
* fetch translations from Transifex
* fix account settings version update routine
2016-06-19 19:15:38 +02:00
Ricki Hirner ea1bef4764 Better group support
* change group methods to less specific values
* new account settings version: change group method to CATEGORIES for updated accounts
* change group method from CATEGORIES to GROUP_VCARDS automatically when a group VCard is received

GUI:
* AccountSettings: disable CalDAV/CardDAV options when the corresponding service is not available
* AccountSettings: new option to choose contact group method
* account setup: allow to choose contact group method at account creation
2016-06-19 18:52:56 +02:00
Ricki Hirner 1c61f537d2 Merge branch 'master' into vcard4-groups 2016-06-12 15:52:07 +02:00
Ricki Hirner 123aea3766 Version bump to 1.0.9.2
* version bump to 1.0.9.2
* fetch translations from Transifex
2016-06-11 09:06:21 +02:00
Ricki Hirner e5bf898656 Make use of RFC6868 optional
* make use of RFC6868 for VCards optional because some defect servers don't accept it
* minor UI improvements (thanks biociahi)
2016-06-11 08:53:29 +02:00
Ricki Hirner 369eab19d1 Switch from preference-v7 to preference-v14 to make preferences look more Material (thanks biociahi!) 2016-06-11 08:53:15 +02:00
Ricki Hirner 81563cf62c Switch from preference-v7 to preference-v14 to make preferences look more Material (thanks biociahi!) 2016-06-09 10:13:47 +02:00
Ricki Hirner 6542ea3bc3 Basic support for VCard4-style groups
* rewritten contact group support to support VCard3 CATEGORIES and VCard4-style KIND/MEMBER groups
* new account setting: contact group method (VCard3/VCard4/Apple "VCard4-as-VCard3")
* keep unknown properties when saving/generating VCards
2016-06-08 21:44:31 +02:00
Ricki Hirner 586621dbba Upgrade to okhttp 3.3.1 2016-05-30 12:02:04 +02:00
Ricki Hirner ea3f5d44a6 Better alarm handling
* ical4android: better alarm handling
* API change: pass OutputStream instead of returning it
2016-05-28 16:00:23 +02:00
Ricki Hirner c9a7c71362 Revert "Contact/event/task upload: use streams directly without extra byte[] array"
This reverts commit 3bde3758fc.
Version bump to 1.0.9.1

Reason: A server MAY reject a request that contains a message body but not a Content-Length
by responding with 411 (Length Required). (RFC 7230 3.3.3 Message Body Length)
2016-05-26 22:08:22 +02:00
Ricki Hirner 60ff9dae1c Revert "Contact/event/task upload: use streams directly without extra byte[] array"
This reverts commit 3bde3758fc.
2016-05-26 22:04:03 +02:00
Ricki Hirner 8c846f82b1 Version bump to 1.0.9
* upgrade to okhttp 3.3 to reduce HTTP/2 incompatibilties
* vcard4android: enable RFC 6868 support in ez-vcard
* minor improvements and bug fixes
* new translations from Transifex
* version bump to 1.0.9
2016-05-25 21:37:43 +02:00
Ricki Hirner d438d2bbdb Contact/event/task upload: use streams directly without extra byte[] array 2016-05-23 15:10:35 +02:00
Ricki Hirner 8456984341 Logs: show which contact/event/task is being prepared for upload 2016-05-23 14:28:27 +02:00
Ricki Hirner 707d03e481 Minor improvements
* use weak references for DavService RefreshingStatusListener
* additional null checks for cases which shouldn't appear, but apparently appear
* additional database conflict handling for cases which shouldn't appear, but apparently appear
* setup by URL: null check for empty host names
* vcard4android: upgrade to ezvcard 0.9.10
2016-05-20 21:38:04 +02:00
Ricki Hirner 53ef0c5261 Work around unexpected IllegalArgumentException when user enters garbage host name 2016-04-30 12:39:19 +02:00
Ricki Hirner 9098d1e051 Always update all task fields (including null values)
* ical4android: always update all task fields (including null values)
* lint optimizations
* fetch translations from Transifex
* version bump to 1.0.8
2016-04-26 23:32:24 +02:00
Ricki Hirner feaaf88940 Introduce local unit tests
* split tests into Android tests and local unit tests
* LoginCredentialsFragment: check for empty host before doing IDN conversion
2016-04-26 13:10:42 +02:00
Ricki Hirner 63f3881f44 Minor optimizations
* catch IllegalArgumentException from HttpUrl in DavResourceFinder (caused crash when logging in with email "test@server/withslash")
* use IteratorChain in DavService collection enumeration
2016-04-19 21:56:05 +02:00
Ricki Hirner b04c098398 Find collections when they're identical with their home set 2016-04-18 00:04:18 +02:00
Ricki Hirner 82b1619dcd Task list synchronization conditions, tests
* task list synchronization: don't set VISIBLE=1 and SYNC_ENABLED=1 at every sync, but only at creation
* task list synchronization: sync only task lists which are SYNC_ENABLED
* honor "manage calendar colors" account setting for task list colors, too
* add run-tests-connected.sh, to be used as pre-commit hook
* ical4android/vcard4android tests
* fetch translations from Transifex
* version bump to 1.0.7
2016-04-14 20:37:22 +02:00
Ricki Hirner 0b53d77228 Changes in ical4android and vcard4android
* don't set ORGANIZER for events without attendees
* make some lists public final instead of @Getter private
* PermissionsActivity: call refresh in onResume() instead of onCreate()
2016-04-13 14:15:34 +02:00
Ricki Hirner e1e95f0c2a Version bump to 1.0.7-beta1
* fetch translations from Transifex
2016-04-10 20:43:01 +02:00
Ricki Hirner 1209dd67ae More detailled OpenTasks installation hint
* show "reinstall DAVdroid" hint only for Android <6
* fetch translations from Transifex
2016-04-10 18:50:42 +02:00
Ricki Hirner b59fb55033 Implement Android 6-style permissions
* increase target API level to 23 (Android 6), which makes Android 6-style permissions mandatory
* AUTHENTICATE_ACCOUNTS permission is only required up to API level 22
* new activity: PermissionsActivity which shows missing permissions and provides buttons to request them
* DavService: Android shouldn't send a null Intent, but sometimes it does, so implement null check
* LocalTaskList: tasksProviderAvailable may return true on API level 23+ even if permissions are not sufficient
* SyncAdapterService: show a notification (with Intent for PermissionsActivity) when permissions are not sufficient
* when creating accounts, set OpenTasks sync always to true if API level is 23+ (even if OpenTasks is not installed [yet])
* update Lombok
2016-04-10 15:55:11 +02:00
Ricki Hirner 15985b5fcb Fetch translations from Transifex
* fetch translations
* minor changes (lint)
2016-04-07 08:38:09 +02:00
Ricki Hirner ee78ee3f53 New feature: only sync in WiFi
* new setting: only sync in WiFi (or when sync is triggered manually)
* new setting: only sync in specific WiFI (by SSID)
* lower default sync interval when account is created to 4 hours (was 1 day)
* version bump to 1.0.6
2016-04-06 21:04:16 +02:00
Ricki Hirner 8cd52bd115 Various tests 2016-04-05 23:25:18 +02:00
Ricki Hirner 5ef81de00e Check for migrations only when package is replaced, DB fixes
* AccountSettings$AppUpdatedReceiver: check for migrations only when package is replaced
* SyncAdapter: move DB helper from service to SyncAdapter to prevent databases from being closed too early
* Manual sync button: run sync immediately (without queueing)
2016-04-05 16:52:43 +02:00
Ricki Hirner 23525c9da0 SyncManager notifications: create a unique notification for every synced collection 2016-04-01 17:45:57 +02:00
Ricki Hirner 63dc698a92 Add account setting: manage calendar colors 2016-03-31 20:07:36 +02:00
Ricki Hirner c6540b3f97 Sync database optimizations
* enable WAL as early as possible
* don't close database in SyncAdapter but only in SyncService
* version bump to 1.0.4
2016-03-31 14:08:48 +02:00
Ricki Hirner cae3856edb OOM handling, DB transactions, calandar VISIBLE, service refresh notification
* handle and show OutOfMemoryErrors correctly (they're not Exceptions)
* use db.beginTransactionNonExclusive() because WAL is enabled
* set calendar VISIBLE=1 AND SYNC=1 only at creation and not at every sync
* update PendingIntent of service refresh notification
2016-03-31 12:47:43 +02:00
Ricki Hirner 14602e6743 Use last path segment as collection display name if there's no DAV:displayName
* use last path segment as collection display name if there's no DAV:displayName
* add Contacts Provider Settings again to show contacts without groups in all clients (bluetooth cars etc.)
2016-03-31 00:52:10 +02:00
Ricki Hirner bc431edeac Add account info when creating calendars
* add ACCOUNT_NAME and ACCOUNT_TYPE when creating calendars
* close TaskProvider when checking for its presence
* when TaskProvider is not available/accessible, explicitly disallow task sync at account creation
  to prevent further crashes
* try to handle OutOfMemoryError
* version 1.0_2
2016-03-30 16:11:44 +02:00
Ricki Hirner 6a812e2993 Fix NPE for synchronization while CalDAV/CardDAV services is not available in DB 2016-03-30 16:07:41 +02:00
Ricki Hirner 0ddc916d1c Fetch translations from Transifex
* version 1.0_1 for commercial stores
2016-03-29 15:39:10 +02:00
Ricki Hirner bb477b3f15 Resource detection: fix NPE 2016-03-29 15:19:53 +02:00
Ricki Hirner c5664d29db Version bump to 1.0
* fetch translations from Transifex
2016-03-27 15:55:05 +02:00
Ricki Hirner e5cc346b70 Verbose logging of resource detection
* enable verbose logging of resource detection
* dav4android: prevent leaking connections
2016-03-25 16:27:43 +01:00
Ricki Hirner a3e702afdc Handle invalid accounts where accounts are used
* add InvalidAccountException for invalid (=not existent/invalid settings version) accounts
* handle invalid accounts properly
* HttpClient: add constructors without Account when authentication is not needed
* drop upgrade compatibility for accounts without version (version<1)
2016-03-25 15:23:54 +01:00
Ricki Hirner 5310e8ec87 Version bump to 1.0-rc1
* fix migration bug (doesn't set read-only flag)
* unify progress dialogs
* improve debug info report styling
2016-03-24 21:03:10 +01:00
Ricki Hirner 288df32654 Import translations from Transifex 2016-03-24 19:32:45 +01:00
Ricki Hirner 228ddaa0c0 Re-initialize logger in :sync process, too (IPC using broadcast)
* re-initialize logger in :sync process after changing the settings (IPC using broadcast)
* move settings from SharedPreferences (which is not multi-process-safe) to ServiceDB
* logger: show exception details
* settings: show debug info
2016-03-24 19:10:30 +01:00
Ricki Hirner 24ce0c1af5 Minor refactoring (lint) 2016-03-24 17:43:35 +01:00
Ricki Hirner ace720f269 Implement AboutActivity (license information) 2016-03-24 13:48:43 +01:00
Ricki Hirner f42075257b Show notifications on refresh errors
* show notifications on DAV service refresh errors
* add Twitter to navigation drawer
2016-03-23 22:01:52 +01:00
Ricki Hirner 6fd6279691 Improve resource detection
* honour calendar-proxy-read/write-for property
* ignore errors when quering member groups for home sets
* remove home sets and collections from the service database not only on 404, but 403, 404 and 410
* fix crash bug when <displayname> was defined, but empty
2016-03-23 14:46:13 +01:00
Ricki Hirner db07c6099e Respect read-only flag of collections
* handle read-only information properly
* don't show (clear-text) password in account settings
2016-03-23 12:30:49 +01:00
Ricki Hirner a4a3f6cee2 SSLSocketFactoryCompat: fix typo 2016-03-23 11:30:42 +01:00
Ricki Hirner 2a7711f3cd Translation fix 2016-03-20 21:03:25 +01:00
Ricki Hirner 99fa342fa6 Version bump to 1.0-beta1
* fetch translations from Transifex
* minor changes (lint/strings)
2016-03-20 18:31:11 +01:00
Ricki Hirner 8e36e25025 Initiate DAV service refresh after migration
* initiate DAV service refresh after migration
* minor refactoring of sync adapter classes
* minor UI changes
2016-03-20 17:41:05 +01:00
Ricki Hirner 5c2a20493b Notification for external file logging
* Show notificatin when external file logging is active
* Use column name constants for ServiceDB access
2016-03-20 11:41:08 +01:00
Ricki Hirner b6b4d02eee Provide settings migration v0.9 -> v1.0 2016-03-19 22:30:07 +01:00
Ricki Hirner 6db1e1edc0 Startup dialogs
* add startup dialogs (F-Droid: donations, Play Store: DRM bug, OpenTasks not installed)
* allow to reset hints/startup dialogs
* AccountSettings: fragment as inner class of activity
2016-03-19 11:22:30 +01:00
Ricki Hirner 9dce028f31 Add SQLite dump to debug report 2016-03-18 19:02:27 +01:00
Ricki Hirner 730a875e23 App settings UI 2016-03-18 17:24:46 +01:00
Ricki Hirner 17fbae2aed Account management: Create address book (similar to create calendar) 2016-03-18 15:40:05 +01:00
Ricki Hirner 9e2bcda06a Allow time-range filtering of events (to the past)
* add account setting + GUI: restrict time range in the past
* add support for restricted time range VEVENT synchronization
* fix bug in handling changed exceptions of recurring events
2016-03-16 18:23:52 +01:00
Ricki Hirner 1b5ba0b28a Minor refactoring 2016-02-24 23:21:25 +01:00
Ricki Hirner 849b75f345 Use java.util.logging instead of sl4fj 2016-02-24 23:08:19 +01:00
Ricki Hirner ba3a5d308d Refactoring
* move AccountSettings up to package context
* HttpClient: take authentication from AccountSettings in the constructor
* App: provide global instance of MemorizingTrustManager
* App: provide global Java logger, optionally with verbose and external file logging
* LoginCredentials: moved from inner-class into setup package
2016-02-24 15:56:30 +01:00
Ricki Hirner a048461fba Refactoring
* make DavResourceFinder.Configuration really serializable
2016-02-24 12:29:07 +01:00
Ricki Hirner 3bb3327543 Show debug info on management errors 2016-02-23 23:10:44 +01:00
Ricki Hirner de4fd5804d Create/delete calendars 2016-02-23 18:42:50 +01:00
Ricki Hirner 821bdba18a Proof of concept: create remote address books, delete remote collections 2016-02-22 14:33:55 +01:00
Ricki Hirner 03d02b25bf AccountActivity changes
* CalDAV/CardDAV resource list views now always contain all elements without scrolling (NonScrollingListView)
* synchronization action in activity now overriddes system sync settings
2016-02-21 14:15:55 +01:00
Ricki Hirner 280c8ba462 Fix crash bug caused by leaking OnAccountsUpdateListener 2016-02-19 14:15:32 +01:00
Ricki Hirner 332eb4b8cc Upgrade to okhttp/3.1.2 + tests 2016-02-19 13:16:34 +01:00
Ricki Hirner f4f7e79dcf Service detection, account settings
* service detection: detect group memberships and query them for homesets
* account settings
* request account synchronization
2016-01-23 18:44:40 +01:00
Ricki Hirner a10649d818 Improved service detection + GUI
* DavService: query group-membership principals for home sets, too
* working collection selection
* contacts sync according to selected address book
2016-01-23 00:04:48 +01:00
Ricki Hirner 6f98fdb76e Selectable calendars 2016-01-20 21:12:37 +01:00
Ricki Hirner e2965c2cbf Update local calendars according to ServiceDB at sync 2016-01-20 15:22:58 +01:00
Ricki Hirner aaef563a74 Update local task lists according to ServiceDB at sync 2016-01-20 15:01:17 +01:00
Ricki Hirner 671a11e479 DavService: refresh collections 2016-01-20 00:39:10 +01:00
Ricki Hirner c1a8cab84e Collections refresh 2016-01-19 20:04:25 +01:00
Ricki Hirner 10411d8afb Add DavService for long-running operations 2016-01-19 13:51:52 +01:00
Ricki Hirner e9bb1b5542 Add account details activity (AccountActivity) 2016-01-18 14:59:19 +01:00
Ricki Hirner dbbc038682 Service database
* HttpClient: authentication that is limited to a host name is never preemptive
* DavResourceFinder: service configuration == null means that this service is not available
* new SQLite database for CalDAV/CardDAV services
* added AccountDetailsFragment, which asks for account name and then finishes account creation
* updated AccountListFragment
2016-01-17 17:10:30 +01:00
Ricki Hirner 53300e3899 Rewrite initial configuration detection
* HttpClient: add Accept-Language header
* HttpClient: fix MemoryCookieStore NullPointerException
* DavResourceFinder: check for home sets, too
2016-01-17 00:34:26 +01:00
Ricki Hirner 3268226a97 Upgrade to okhttp3 2016-01-16 21:34:41 +01:00
Ricki Hirner 9d05592e85 New initial server configuration detection
* separate initial server configuration (= principal and/or a certain collection) detection from collection refresh (to be done)
* GUI: LoginActivity
2016-01-16 00:53:05 +01:00
Ricki Hirner 35c6ccdfa9 Initial changes for new GUI 2016-01-15 01:07:56 +01:00
Ricki Hirner 06a046f520 Append trailing slashes to Web URLs; okhttp upgrade 2016-01-08 17:57:51 +01:00
Ricki Hirner cf5c5d3461 README updates 2016-01-04 01:43:53 +01:00
Ricki Hirner 51f787aa8b Version bump to 0.9.1.3 2016-01-03 01:47:32 +01:00
Ricki Hirner 15b560c22d Upgrade to okhttp/2.7.1 2016-01-02 10:59:52 +01:00
Ricki Hirner 46dc5c7a61 Increase timeout values
* increase timeout values because some servers are known to be very slow
2015-12-06 13:45:15 +01:00
Ricki Hirner 37118784ff BuildConfig: use build time instead of current time for timestamp 2015-11-27 14:04:24 +01:00
Ricki Hirner 2c47780e86 README changes; fix EXDATE bug
* ical4android: process EXDATEs when there are no explicit exceptions
2015-11-27 12:31:15 +01:00
Ricki Hirner 92e3017d13 Show open-source information when MainActivity is created 2015-11-24 18:36:25 +01:00
Ricki Hirner 933fc5e07b Version bump to 0.9.1.2
* debug info: send report inline up to 8000 characters, as attachment otherwise
* ical4android: fix bug which locally deleted tasks by mistake
2015-11-24 17:59:47 +01:00
Ricki Hirner bb611b0fd7 New upstream libraries, task sync bug fix
* use OkHttp 2.6.0, slf4j-android 1.7.13, and ez-vcard 0.9.8
* ical4android: don't delete all tasks instead of single one
2015-11-23 09:07:35 +01:00
Ricki Hirner 8da4725c7c ContactsSyncManager: URL fix
* ContactsSyncManager: don't try to download external resources which do not have a valid URL
2015-11-20 10:12:48 +01:00
Ricki Hirner 39e6fc8373 Increase version code……………………………………………………………………………. 2015-11-16 13:24:44 +01:00
Ricki Hirner 5f79a340ff Version 0.9.1.1
* resource detection: ignore 404 errors when trying context paths
* work around crash when edit field is changed while there is no acitivity (???!)
* dav4android: fix calendar-multiget request
2015-11-16 13:15:57 +01:00
Ricki Hirner 5b1d55aa7b Optimize soft keyboard handling, make resource detection dialog not cancelable 2015-11-09 11:31:40 +01:00
Ricki Hirner 340dcd374c Add basic support for cookies
* add basic support for cookies (doesn't work for URLs with ports: https://code.google.com/p/android/issues/detail?id=193475)
* MemorizingTrustManager: log reason for inaccessible key store files
2015-11-08 18:51:19 +01:00
Ricki Hirner 7434ea0868 Merge branch 'master' of https://gitlab.com/bitfireAT/davdroid 2015-11-07 22:03:54 +01:00
Ricki Hirner 4e83ef73d0 Version 0.9.1-beta1
* CalendarSyncManager/TaskSyncManager: only set calendar name and color on sync when data is available
* DavResourceFinder: test getCurrentUserPrincipal
* dav4android: use java.util.ServiceLoader, resilience against multi-status with <propstat> without <status> + test
* ical4android: always set HAS_ATTENDEE_DATA to 1
* vcard4android: small fixes
* merge translations from Transifex
2015-11-07 22:03:19 +01:00
Ricki Hirner 18f5b8cd5e * CalendarSyncManager/TaskSyncManager: only set calendar name and color on sync when data is available
* DavResourceFinder: test getCurrentUserPrincipal
* dav4android: use java.util.ServiceLoader, resilience against multi-status with <propstat> without <status> + test
* ical4android: always set HAS_ATTENDEE_DATA to 1
* vcard4android: small fixes
* merge translations from Transifex
2015-11-07 21:09:59 +01:00
Ricki Hirner c922291d33 Version bump to 0.9.1
* filter ":" and "/" from external log file names
2015-11-07 15:35:22 +01:00
Ricki Hirner 4f07ca3ca0 Various improvements
* ContactsSyncManager: gracefully handle photo URLs without host name
* MainActivity: cache installer package name
* dav4android: use java.util.ServiceLoader to load DAV property factories
2015-11-07 15:18:23 +01:00
Ricki Hirner 2db80a8b7a Amend DebugInfoActivity
* write report to temporary file in external cache dir before sending
* don't delete the report file onActivityResult (because services like the email service may access it asynchronously)
* don't show label of installer (just the package name), because some use ambiguous strings like "App Store" etc.
* show sync. settings for all accounts again
2015-11-07 14:10:17 +01:00
Ricki Hirner 7b3d1a66b8 Fix crash bug when external log file can't be created 2015-10-28 14:10:03 +01:00
Ricki Hirner 24768626fa Update .gitmodules to publically accessible URLs 2015-10-24 12:08:51 +02:00
Ricki Hirner 1fc8ef0df7 Version 0.9.0.4
* ical4android: treat empty-string task location and URL as null values
* vcard4android: ignore raw contact data without MIMETYPE
* gracefully ignore when server doesn't sent Content-Type in GET responses
* merge translations from Transifex
2015-10-24 00:36:22 +02:00
Ricki Hirner 26cb32df9b vcard4android: ignore raw contact data rows without MIMETYPE 2015-10-23 02:28:23 +02:00
Ricki Hirner 6e2152d79f Fallback to PROPFIND when REPORT addressbook-query returns 400, 403, 500 or 501
* increase max. log line length to 80 characters
2015-10-21 14:40:03 +02:00
Ricki Hirner 2fee1583c7 Merge translations from Transifex and bump version to 0.9.0.3 2015-10-21 02:15:10 +02:00
Ricki Hirner e840f0fc2d Minor bug fixes and improvements
* Contacts sync: if REPORT addressbook-query doesn't work, don't ignore other exceptions than HTTP 40x errors
* dav4android: Digest auth improvements (e.g. for OS X Calendar Server)
* vcard4android: better support for exotic IMPP handles and names
2015-10-21 02:06:29 +02:00
Ricki Hirner 1a2c8df821 Version bump to 0.9.0.2 2015-10-20 13:28:34 +02:00
Ricki Hirner 4e33a62c1d Better handling of contacts without N/FN 2015-10-20 13:23:01 +02:00
Ricki Hirner c7460dc9e1 Lower target SDK to 22 (pre-M) to fixes crashes on Android 6; new permissions model will be implemented later 2015-10-20 12:56:48 +02:00
Ricki Hirner 605d692b00 2 bug fixes
* ical4android: enumerate (=synchronize) all task lists and not only the first one
* fix crash bugs when activating external logging without external storage
2015-10-20 12:04:31 +02:00
Ricki Hirner df7d3e9c3d Version bump to 0.9.0.1
* with minor fixes
2015-10-19 19:04:01 +02:00
Ricki Hirner 37f7f7fff3 SSLSocketFactoryCompatTest 2015-10-19 16:55:01 +02:00
Ricki Hirner 31493650fb Handle event/task sequence == null (meaning it was created locally and not sequence has yet been assigned) 2015-10-19 16:44:37 +02:00
Ricki Hirner 3bd2989fec Enable TLSv1.1 and TLSv1.2 (if available) for Android <5 again 2015-10-19 15:16:44 +02:00
Ricki Hirner b30e99d18c Remove Robohydra (obsoleted by okhttp-mockwebserver) 2015-10-19 13:18:21 +02:00
Ricki Hirner 6e75ca0281 Fix lint warnings, don't require external storage permission for SDK >18 2015-10-19 11:57:43 +02:00
Ricki Hirner fcb381b970 Resource detection fixes
* check TXT records for <service>._tcp.domain.tld instead of domain.tld
* duplicate log to ADB for successful resource detection
2015-10-19 01:12:06 +02:00
Ricki Hirner f2d5ae81fe Version 0.9 ready!
* fix lint warnings
* line-break too long messages of network trace logs
* DebugInfoActivity "send": attach log file instead if sending it as plain text
* revert to ez-vcard 0.9.6 because of https://github.com/mangstadt/ez-vcard/issues/33
* German translations
2015-10-19 00:19:29 +02:00
Ricki Hirner 950b5f3155 Changed source strings 2015-10-18 19:36:03 +02:00
Ricki Hirner 2f704b11ea Log resource detection results to viewable string
* new StringLogger
* DavResourceFinder: log to StringLogger; if no collections are found, logs can be views
* DebugInfoActivity: show passed logs
* script to fetch translations from Transifex
* increase version to 0.9-beta2
2015-10-18 17:30:26 +02:00
Ricki Hirner c37c219afc Synchronization logging to external file
* use ExternalFileLogger to log synchronization, if enabled in Settings
* new settings: log to external file / log verbose
* DavResource: check for well-known even if service type of user-given URL can't be determined
* remove oblsete testing assets
2015-10-18 16:20:26 +02:00
Ricki Hirner c99ab38439 Merge translations from Transifex 2015-10-17 22:42:45 +02:00
Ricki Hirner 127e32732c Improve DavResourceFinder
* check whether user-given URL actually provides CalDAV/CardDAV before trusting the current-user-principal
  as there may be different principals for CalDAV and CardDAV (if both services are completely separated)
2015-10-17 19:13:16 +02:00
Ricki Hirner 557e69f1cc Sync manager optimization
* allow cancellation of synchronization within appropriate time
* sync error notification: use loader, show all accounts, show whether JB Workaround is installed, reorder
2015-10-17 11:33:35 +02:00
Ricki Hirner a68717de44 Group support (VCard 3 CATEGORIES) with vcard4android
* VCard 3-style group support (CATEGORIES)
* sync error notification improvements
* some tests
2015-10-16 23:06:35 +02:00
Ricki Hirner 2f87b51f57 Support Basic and Digest auth 2015-10-16 19:30:50 +02:00
Ricki Hirner 46a2880e0d New collection/service discovery: CalDAV+CardDAV 2015-10-16 12:40:44 +02:00
Ricki Hirner 275335ad26 New resource detection
* new resource detection: only CalDAV yet
2015-10-16 03:27:56 +02:00
Ricki Hirner f824386db4 Improve error/account settings notifications
* move address book settings from account user data to ContactsContract.SyncState
* remove "VCard4 capable?" setting (as it's detected at every sync)
* show user notification when updating settings version or when Android version was increased
* improve stack trace in DebugInfoActivity
* get rid of Guava (use Commons again)
2015-10-15 15:36:55 +02:00
Ricki Hirner 68ee33b2f9 Resource detection, bug fixes
* resource detection is subject to change yet
* don't use UID_2445 for Android <= 4.1
* more useful sync error notification messages
* handle 401 Unauthorized and show account info when notification is tapped
2015-10-15 13:46:19 +02:00
Ricki Hirner fa766906c8 Tasks with new sync logic 2015-10-15 00:49:15 +02:00
Ricki Hirner 65c15258ca Process recurring events, exceptions etc. 2015-10-14 21:45:19 +02:00
Ricki Hirner de7ca7b91c Basic implementation of calendar sync. with common SyncManager 2015-10-14 18:20:51 +02:00
Ricki Hirner fa4f090ff7 Remove legacy calendar/task/WebDAV code 2015-10-14 13:38:18 +02:00
Ricki Hirner c71dc2ba7e New DebugInfoActivity
* DebugInfoActivity shows and allows to share sync exceptions
* log sync phase
2015-10-14 12:23:02 +02:00
Ricki Hirner c90a340159 README changes 2015-10-13 11:27:33 +02:00
Ricki Hirner f2a5bce53c Integrate MemorizingTrustManager by Georg Lukas 2015-10-13 02:34:24 +02:00
Ricki Hirner bdddd23441 Contacts sync logic
* download external resources (contact images)
* improve ETag handling
* contacts: set UNGROUPED_VISIBLE to 1
2015-10-12 14:16:26 +02:00
Ricki Hirner c27443d921 Contact synchronization logic
* use VERSION_CODE and buildTime from BuildConfig
* new HTTP User-Agent, VCard PRODID values
* contact sync: store CTag in SyncState
* sync logic: upload contacts, check CTag, multiget
2015-10-12 01:59:05 +02:00
Ricki Hirner 652f98848d New sync logic for ContactsSyncAdapter, using dav4android and vcard4android 2015-10-11 22:34:03 +02:00
Ricki Hirner a4d1c2ab86 First implementation of CardDAV sync with dav4android and vcard4android
* try to get rid of Apache Commons
2015-10-10 23:30:38 +02:00
Ricki Hirner 0d26eac245 Resource detection with dav4android
* handle authentication (only Basic auth yet)
* rewrite DavResourceFinder to use dav4android
2015-10-10 15:47:44 +02:00
Ricki Hirner 5aa7caec99 First use of dav4android for resource detection
* replaced Apache httplib by gradle version because it will be removed completely anyway
2015-10-10 02:15:53 +02:00
Ricki Hirner 448e052912 Make well-known URLs work again when user enters an initial context path 2015-09-22 12:19:39 +02:00
Ricki Hirner b800556607 Merge branch 'french_translations' of https://github.com/callmemagnus/davdroid 2015-09-15 17:53:23 +02:00
Ricki Hirner d9855190cb Fix UI crash bug 2015-09-15 17:37:36 +02:00
Magnus Anderssen bc96a7f305 Added missing french translations 2015-09-06 21:22:43 +02:00
Ricki Hirner 4910d59c21 Merge pull request #628 from gjtoth/master
Hungarian translation updated.
2015-09-06 13:16:02 +02:00
Ricki Hirner 73f75cebed Update README 2015-09-05 01:35:02 +02:00
Ricki Hirner d6e375361f Version update to 0.8.4.1
* minor (crash) bug fixes
* updated translations
2015-08-31 16:31:12 +02:00
Gábor J.Tóth fb57c7fd55 Hungarian translation updated. 2015-08-28 17:46:24 +02:00
Ricki Hirner ce61dd224a Version bump to 0.8.4 2015-08-25 22:06:33 +02:00
Ricki Hirner 0e3348c298 Exception handling, verbose TLS logs
* handle IllegalArgumentException in Tasks provider (show LocalStorageException notification) (closes #601)
* add more verbose TLS cipher logs (see #608)
2015-08-25 22:04:45 +02:00
Ricki Hirner 65307f9d02 ical4j update, clean up XML requests
* ical4j update to 2.0-beta1 (fixes #509, fixes #606)
* only run sync adapters in :sync process, set thread context class loaders appropriately
* remove "class" attribute from XML requests (fixes #615)
2015-08-25 21:18:29 +02:00
Ricki Hirner 78b32ee150 Handle attendees and reminders for exceptions of recurring events 2015-08-10 11:54:05 +02:00
Ricki Hirner 34399181aa Refactoring
* VEvent: don't set LAST-MODIFIED to sync time (should be last modification time which is not available)
* ignore 403 Forbidden when uploading (can happen on certain scheduling conditions)
2015-08-10 00:33:26 +02:00
Ricki Hirner 91544b3e87 Remove unnecessary getters/setters
* remove getters/setters for protected fields when they're only accessed from package scope
* version bump to 0.8.3
2015-08-09 20:02:37 +02:00
Ricki Hirner bac737a8f9 Improve ATTENDEE/ORGANIZER handling 2015-08-08 15:39:58 +02:00
Ricki Hirner 31644f47b0 Process Content-Type character set information (fixes #594) 2015-08-06 15:57:06 +02:00
Ricki Hirner dd7d2a6368 Improve event exception handling (always convert RECURRENCE-ID DATE-TIME to DATE when master event is all-day) 2015-08-06 14:11:39 +02:00
Ricki Hirner 0c9251080f Send used VTIMEZONEs with VTODOs 2015-08-03 16:54:10 +02:00
Ricki Hirner 4821107996 Refactoring
* WebDavResource: properties in separate subclass
* improve time zone handling
* always provide task list color
2015-08-03 15:53:19 +02:00
Ricki Hirner fa95bdc6e5 Send charset information with MIME type when uploading VCard/3.0 resources 2015-08-02 16:35:02 +02:00
Ricki Hirner 316102cca4 Detect VCard/4 support per sync, too 2015-08-02 16:24:00 +02:00
Ricki Hirner 8f933362b6 Merge pull request #593 from svetlemodry/master
Czech translation update
2015-08-02 14:47:04 +02:00
Jaroslav Lichtblau 8e3cfedf94 Czech translation update
for davdroid
2015-08-02 12:33:41 +02:00
Ricki Hirner 2be029a4f8 Added uninstall warning in "Install Tasks app" string (fixes #589) 2015-08-02 09:29:28 +02:00
Ricki Hirner 9e5e18f20a Update collection properties (name, color) on every sync 2015-08-02 08:57:03 +02:00
Ricki Hirner 062bfc6007 Convert RDate/ExDate properties <-> Android RDATE/EXDATE strings more precisely (+ tests) 2015-08-01 13:25:35 +02:00
Ricki Hirner f7cc59ebdb Version bump to 0.8.2 2015-07-29 21:59:36 +02:00
Ricki Hirner 76c943718e VEVENT exceptions always get master UID
* make sure that VEVENT exceptions always the the UID of the master event (fixes #523)
2015-07-28 18:19:41 +02:00
Ricki Hirner 25675d4567 Asset downloader: send credentials when URI authority is the same, even if the default port is explicitly given 2015-07-28 16:23:15 +02:00
Ricki Hirner ca68e59b60 Improved iCal generation
* move shared code to new iCalendar class
* generate UIDs and file names with "_" instead of "@" to reduce encoding problems (closes #585)
* tasks: validate "start date" and "completed at" time zones
2015-07-28 15:29:54 +02:00
Ricki Hirner 54497ffbcc Fix problem of recent commit with deleting local records 2015-07-28 15:04:57 +02:00
Ricki Hirner 050bc48454 Merge branch 'master' of github.com:bitfireAT/davdroid 2015-07-28 14:48:32 +02:00
Ricki Hirner 9d835950c6 Merge pull request #580 from oskarjakiela/master
Add Polish translation
2015-07-28 14:48:02 +02:00
Oskar S. Jakieła f76d15cc82 Add Polish translation 2015-07-20 21:12:53 +02:00
Ricki Hirner f57f2a16f5 Small refactoring 2015-07-18 15:15:36 +02:00
Ricki Hirner b38b719a46 Null-pointer check for SIP address types (fixes #506) 2015-07-18 00:52:44 +02:00
Ricki Hirner 3779d5c9b2 Handle 409 Conflict status codes (fixes #563) 2015-07-07 00:25:49 +02:00
Ricki Hirner 1c6a3517b5 Version bump to 0.8.1
* use slf4j-android as it's required by ical4j/2
* disable i18n lint warnings
* retain ServerInfo when activity is re-created (fixes #543)
2015-07-06 23:48:35 +02:00
Ricki Hirner 1c6a7a4b50 Add trailing slash to sample URL (closes #522) 2015-07-06 00:27:00 +02:00
Ricki Hirner afd0fedfca Specify encoding details of member names passed to WebDavResource (fixes #482) 2015-07-05 23:51:53 +02:00
Ricki Hirner d8c6b971e8 Merge pull request #504 from svetlemodry/master
Czech translation for davdroid
2015-07-05 22:18:06 +02:00
Ricki Hirner 723982e327 Library updates
* use ical4j/2.0.x instead of 1.0.x (thanks @benfortuna)
* use Apache Commons 3.x instead of 2.x
* code optimizations
2015-06-14 20:35:28 +02:00
Ricki Hirner 1a91c098e5 Check for null values of Events.ORIGINAL_ALL_DAY (should fix #551)
* fix indentation
2015-06-14 12:39:36 +02:00
Ricki Hirner b7d692732b Check for null values in relations (should fix #547) 2015-06-14 12:07:04 +02:00
Ricki Hirner ea8783cc1f Check for null values in StructuredPostal.TYPE (should fix #549) 2015-06-14 12:00:34 +02:00
Ricki Hirner 5bc2287b66 Don't disable per-session cookie management + test (closes #525) 2015-06-12 00:41:21 +02:00
Ricki Hirner 6ec0e6119f Set _DIRTY=0 for new tasks explicitly (fixes #524) 2015-06-11 23:58:03 +02:00
Ricki Hirner 7309e6ab0b Merge pull request #519 from pejakm/srup
Update Serbian translation
2015-05-27 23:46:29 +02:00
Mladen Pejaković 00ee1a71e5 Update Serbian translation 2015-05-27 23:20:36 +02:00
Ricki Hirner bfc5c9237b Add missing files (fixes #517) 2015-05-27 15:51:31 +02:00
Ricki Hirner 73b3cb469c Merge pull request #518 from phy25/values-zh-CN
Translations in zh-rcn for v0.8.0
2015-05-27 15:07:23 +02:00
phy25 0325eaacb2 Translations in zh-rcn for v0.7. 2015-05-27 20:22:27 +08:00
Ricki Hirner 99d13b9782 Extra icon for tasks in "Select collections" fragment 2015-05-27 12:04:06 +02:00
Ricki Hirner 1621a9ab5d Version bump to 0.8.0
* update to Lombok 1.16.4 and dnsjava 2.1.7
* optimize imports and copyrights
* delete Note data class (will be implemented later)
2015-05-27 11:21:31 +02:00
Ricki Hirner 781759ae84 Version bump to 0.8.0-beta3
* don't offer to install Tasks from InstallAppsFragment, show instructions instead
* fix crash when displaying "todo lists" heading in "selection collections" fragment (fixes #512, fixes #513)
* use Android SDK build tools v22.0.1
2015-05-27 10:13:03 +02:00
Ricki Hirner d1b29554f9 Sync tasks
* remove VJOURNAL/notes sync (will be implemented later)
* setup: add "install Tasks app" fragment
* version bump to 0.8.0-beta1
* use Tasks instead of Mirakel
* handle task list colors
* allow independent selection of calendar/task sync for the same CalDAV calendar
* minor refactoring (don't use return value of Builder)
* handle more task fields and time zones
* sync interval setting for tasks
2015-05-25 19:54:16 +02:00
Ricki Hirner e29920504e Sync notes and tasks 2015-05-22 03:06:30 +02:00
Jaroslav Lichtblau 69ef299208 Czech translation for davdroid
updated
2015-05-17 11:48:40 +02:00
Ricki Hirner c1671b7d62 Implement remote filters to fetch only CalDAV resources with useful components (VEVENT for now) 2015-05-15 23:35:27 +02:00
Ricki Hirner 1326e8bbdc Version bump to 0.7.7
* SettingsActivity: up navigation
* tests
2015-05-15 14:47:03 +02:00
Ricki Hirner 290f36619c Handle Android "INTEGER (boolean)" values which are read as CharSequences correctly (fixes #503) 2015-05-15 02:31:53 +02:00
Ricki Hirner ed3cd9a4ba Merge pull request #498 from dehart/master
Added dutch translation file
2015-05-12 18:42:59 +02:00
Michael de Hart 63ff1453d2 Added dutch translation file 2015-05-12 14:56:29 +02:00
Ricki Hirner 7f5bfc975c Version bump to 0.7.6
* additional test
* minor code optimizations
2015-05-09 13:29:28 +02:00
Ricki Hirner bd92949f3c Process multiple RDATE/EXDATE values (see #340, see #495) 2015-05-09 11:55:35 +02:00
Ricki Hirner 57ea496fe3 EXDATE processing
* don't ignore the time zone of EXDATEs (fixes #495)
2015-05-08 17:46:10 +02:00
Ricki Hirner cea35a9162 Better reminder (VALARM) handling
* handle WEEKS in duration correctly (fixes #398)
* handle positive and negative TRIGGER duration values correctly
2015-05-03 22:44:15 +02:00
Ricki Hirner 02cf7d8603 Use EntityEvent to populate entities from local DB 2015-05-03 21:56:31 +02:00
Ricki Hirner 181be62ef3 Use RawContactsEntity to query raw contact data 2015-05-03 17:09:20 +02:00
Ricki Hirner 346b918fac RDATE processing
* don't ignore the time zone of RDATEs (see #340)
2015-05-02 11:39:25 +02:00
Ricki Hirner 2cc5c42b26 New target SDK: API level 22 (Android 5.1) 2015-05-02 10:55:31 +02:00
Ricki Hirner 63839a19dc Support relations the VCard 4.0 way (closes #278) 2015-05-01 02:47:08 +02:00
Ricki Hirner d4f06c1b37 Version bump to 0.7.5
* account settings: show whether CardDAV server supports VCard 4.0
* CardDAV GET: ask for VCard 3.0 or VCard 4.0 (preferred) contacts
* CardDAV multiget: ask for VCard 4.0 contacts if the server supports it
* CardDAV PUT: send VCard 4.0 contacts if the server supports it
* import Apache httpclient-android rev. 1652769 correctly (hopefully fixes #491)
2015-05-01 00:36:12 +02:00
Ricki Hirner d7dc909859 Use source version of apache-httpclient
We can't use a repository version because there's no release yet which
contains rev. 1652769. However, this revision is necessary to get SNI with Android <4.2, too.
To avoid packaging a pre-compiled jar, this source lib has been added as a subproject:

apache-httpclient, branch 4.3.5-android, revision 1652769

Fixes #491.
2015-04-30 14:48:28 +02:00
Ricki Hirner a13dbb7164 Process <status> in multi-get responses without <propstat> (see #475) 2015-04-29 20:04:56 +02:00
Ricki Hirner 1f9b8a296a Merge branch 'master' of github.com:bitfireAT/davdroid 2015-04-29 00:25:49 +02:00
Ricki Hirner 6a8326c36e Version bump to 0.7.3 2015-04-29 00:25:07 +02:00
Ricki Hirner d3dbbf4e17 Test parsing recurring events with exceptions
* adapted tests
* use org.apache.httpcomponents:httpclient-android:4.3.5.1 again because it seems to contain all necessary fixes
2015-04-29 00:21:15 +02:00
Ricki Hirner 071151012c Merge pull request #490 from FabianoK/master
Portugues Brazilian translation
2015-04-28 22:29:49 +02:00
Fabiano Sardenberg Kuss - COCOE/COASC/COSAM 50c93c7ad8 Portugues Brazilian translation 2015-04-28 17:16:52 -03:00
Ricki Hirner 7eaa6a6bb3 Synchronize exceptions of recurring events to the Calendar storage (server to client)
* Event class finds and processes exceptions of recurring events
* workaround for iCloud and other services that provide RECURRENCE-ID as DATETIME even if the original event is an all-day event
* VEvents are generated with all time zone definitions (including time zone definitions of exceptions)
2015-04-28 13:50:06 +02:00
Ricki Hirner 97c1bf9ed9 Handle dirty flag of exceptions of recurring events
* mark events as dirty when its exceptions are dirty/deleted
* when (mass-)deleting events, delete corresponding exceptions too
2015-04-28 11:08:22 +02:00
Ricki Hirner 4bbebe98fe Sync recurring event exceptions to CalDAV server
* added SQL filter possibility to generic LocalCollection
* added exceptions of recurring events to Event
* process exceptions of recurring events in LocalCalendar
2015-04-28 01:36:01 +02:00
Ricki Hirner 550adf78d1 Merge pull request #486 from astalavister/master
Russain language added
2015-04-25 21:36:16 +02:00
astalavister a92050bc5e Russian language translation v. 0.7 2015-04-24 12:41:53 +08:00
astalavister 4c5bde0a28 Russian language translation v. 0.7 2015-04-24 12:32:01 +08:00
Ricki Hirner 32eafc096d Generate VCARD N property for prefix- and suffix-only contacts, too (closes #469) 2015-03-29 15:13:10 +02:00
Ricki Hirner e3c66e00ef Version bump to 0.7.2
* catch illegal SIP addresses (fixes #470)
* version bump to 0.7.2
2015-03-29 15:05:56 +02:00
Ricki Hirner 8034dc454c Improve time-zone detection in VEVENTs 2015-03-29 14:57:12 +02:00
Ricki Hirner 1c069a2efb Merge branch 'master' of github.com:bitfireAT/davdroid 2015-03-29 14:44:34 +02:00
Ricki Hirner d88e53ac62 Merge pull request #473 from Springuin/master
Time zone guessing improved, fixes 'Assuming time zone Etc/GMT for Etc/G...
2015-03-29 14:44:25 +02:00
Ricki Hirner d4510e0d1e Correctly detect address-book and calendar home-sets which are address books/calendars themselves 2015-03-29 14:39:47 +02:00
Marc de Hoop 17a2fa679e Time zone guessing improved, fixes 'Assuming time zone Etc/GMT for Etc/GMT-2' 2015-03-28 10:15:40 +01:00
Ricki Hirner fcec61cc45 Merge pull request #471 from xphnx/patch-3
Update spanish translation
2015-03-23 16:04:21 +01:00
xphnx dd2bc3043d Update spanish translation 2015-03-23 15:27:27 +01:00
Ricki Hirner cca1319ce2 Merge pull request #467 from pejakm/srtr
Update Serbian translation
2015-03-13 18:33:37 +01:00
Mladen Pejaković a7a070af46 Update Serbian translation 2015-03-13 15:47:18 +01:00
Ricki Hirner 3faa7913d4 Merge pull request #466 from gjtoth/master
Updated Hungarian translation
2015-03-12 23:24:03 +01:00
Gábor J.Tóth 2e8bef7091 Updated Hungarian translation. 2015-03-12 18:48:02 +01:00
Ricki Hirner 1c59bca599 Bug fixes
* show "Settings" in Android Settings/Accounts again
2015-03-12 18:25:10 +01:00
Ricki Hirner d10b805881 Minor UI changes
* always notify user on SSL/TLS exceptions (closes #447)
* re-organize UI classes to separate ui package
* re-organize layout and menu resources
* make MANAGE_NETWORK_USAGE intent filter work
2015-03-12 18:25:10 +01:00
Ricki Hirner d86379b72a Fix Settings rendering issues
* fix Settings rendering issues (fixes #459)
* version bump to 0.7.1
2015-03-12 18:25:10 +01:00
Jaroslav Lichtblau cd382a9b0a Czech translation update for v0.7 2015-03-12 18:25:09 +01:00
phy25 c9f33975b1 Translations in zh-rcn for v0.7. 2015-03-12 18:25:09 +01:00
Ricki Hirner 54abf4ad8d Version 0.7
* new Settings activity
* Settings: display/change user name, password, preemptive auth.
* Settings: display/change sync. interval for contacts and calendars
* requires permission GET_ACCOUNTS to list accounts in Settings
* requires permission READ_SYNC_SETTINGS to display current sync intervals
* remove obsolete files from res/
* update copyright notices
* version bump to 0.7
2015-03-12 18:25:09 +01:00
Ricki Hirner 15beaadba0 New version: 0.6.12
* if well-known URI detection fails with I/O errors, ignore it instead of aborting
* if current-user-principal is not supported by the server, use the user-given URL is the principal URL
* determine availability of CalDAV/CardDAV by whether a calendar/address-book home set is available
* if DAV:displayname is not available for a collection, use its URL path instead
* remove SSLPeerUnverifiedException handling (Apache HttpClient doesn't use this exception)
* use Depth: 1 (instead of Depth: 0) for multi-get REPORT requests
* don't display address books in "Select collections" UI if CardDAV is not available
* don't display calendars in "Select collections" UI if CalDAV is not available
* version bump to 0.6.12
2015-03-12 18:25:08 +01:00
Ricki Hirner c924d24320 Bug fixes
* show "Settings" in Android Settings/Accounts again
2015-03-11 14:56:39 +01:00
Ricki Hirner 36673c0020 Minor UI changes
* always notify user on SSL/TLS exceptions (closes #447)
* re-organize UI classes to separate ui package
* re-organize layout and menu resources
* make MANAGE_NETWORK_USAGE intent filter work
2015-03-11 14:34:13 +01:00
Ricki Hirner 146f70959d Fix Settings rendering issues
* fix Settings rendering issues (fixes #459)
* version bump to 0.7.1
2015-03-11 14:01:43 +01:00
Ricki Hirner 7df3967b2f Merge pull request #461 from phy25/values-zh-CN
Translations in zh-rcn for v0.7
2015-03-11 12:20:32 +01:00
Ricki Hirner c5ea2a9de9 Merge pull request #462 from svetlemodry/master
Czech translation update for v0.7
2015-03-11 12:20:16 +01:00
Jaroslav Lichtblau 54e4732675 Czech translation update for v0.7 2015-03-09 16:34:06 +01:00
phy25 0c2c209437 Translations in zh-rcn for v0.7. 2015-03-09 20:28:52 +08:00
Ricki Hirner ede7fd2033 Version 0.7
* new Settings activity
* Settings: display/change user name, password, preemptive auth.
* Settings: display/change sync. interval for contacts and calendars
* requires permission GET_ACCOUNTS to list accounts in Settings
* requires permission READ_SYNC_SETTINGS to display current sync intervals
* remove obsolete files from res/
* update copyright notices
* version bump to 0.7
2015-03-08 23:37:48 +01:00
Ricki Hirner 9682b535cb New version: 0.6.12
* if well-known URI detection fails with I/O errors, ignore it instead of aborting
* if current-user-principal is not supported by the server, use the user-given URL is the principal URL
* determine availability of CalDAV/CardDAV by whether a calendar/address-book home set is available
* if DAV:displayname is not available for a collection, use its URL path instead
* remove SSLPeerUnverifiedException handling (Apache HttpClient doesn't use this exception)
* use Depth: 1 (instead of Depth: 0) for multi-get REPORT requests
* don't display address books in "Select collections" UI if CardDAV is not available
* don't display calendars in "Select collections" UI if CalDAV is not available
* version bump to 0.6.12
2015-02-13 18:30:00 +01:00
Ricki Hirner 8642003dc5 Download photo URIs in VCards
* download photo URIs in VCards (closes #430); send authorization only when on same server
* version bump to 0.6.11
* minor code optimizations
2015-01-18 20:34:57 +01:00
Ricki Hirner 50271e679a Don't show notifications on soft (I/O) errors
* don't show notifications on soft (I/O) errors (closes #425)
* version bump to 0.6.10.2
2015-01-18 17:35:44 +01:00
Ricki Hirner 699478a513 Use Apache HttpClient-Android SSLConnectionSocketFactory
* SSLConnectionSocketFactory is now SNI-capable, so there's no need
  to manage SNI in DAVdroid
* keep secure protocol/cipher suite definitions
* use a patched jar until https://issues.apache.org/jira/browse/HTTPCLIENT-1591 is fixed
2015-01-18 17:04:32 +01:00
Ricki Hirner 7216415d34 Loosen some restrictions
* re-enable relaxed iCal unfolding and parsing + test
* switch to BrowserCompatHostnameVerifier again to allow IP addresses in certificate CN
2015-01-06 18:42:15 +01:00
Ricki Hirner 09da0df62f Version bump to 0.6.10 2015-01-01 21:51:28 +01:00
Ricki Hirner da83c32bb6 Show basic notifications on sync errors (closes #182)
* only works for Android 4.1+
* pinch in to expand the message
2015-01-01 21:23:31 +01:00
Ricki Hirner 53e0de29bd Handle absolute URIs in resource detection (+ tests) 2015-01-01 18:53:03 +01:00
Ricki Hirner 4de71331b9 TlsSniSocketFactory improvements
* make it work again with non-proxied connections
* add tests for proxied and non-proxied connections
* add tests for SNI, host name and certificate verification
2015-01-01 17:56:21 +01:00
Ricki Hirner f6aa8a2c6c TLS/SNI support, proxy support
* SNI now works for proxied connections, too
* "Cannot verify hostname" message removed and split into two localized messages: one for unknown certificates, the other for hostname mismatch
2014-12-28 16:33:45 +01:00
Ricki Hirner 1d1fe00ee6 Handle resources with "%" and other special characters in file name more accurately (fixes #401) 2014-12-27 22:42:57 +01:00
Ricki Hirner 2b99b82a1b Repair some common invalid URLs, version bump to 0.6.9. invalid URLs, version bump to 0.6.9.1 2014-12-26 19:55:52 +01:00
Ricki Hirner d10fdd5f2a Mention used Apache HttpClient Android port 2014-12-26 18:57:51 +01:00
Ricki Hirner ee5ea0a90b Handle relative URIs with ":" in path names correctly 2014-12-26 18:53:22 +01:00
Ricki Hirner 8c341a18c3 Minor code tweaks (by lint) 2014-12-21 11:24:00 +01:00
Ricki Hirner 358e691457 Temporarily use patched version of HttpClient-Android 4.3.5 to avoid Basic Authorization bug 2014-12-21 10:50:53 +01:00
Ricki Hirner 9ae1d552c9 Use HttpClient HC4 classes whenever possible (instead of stock Android HttpClient 4.0-beta classes); reorganize imports 2014-12-21 02:19:06 +01:00
Ricki Hirner d1fb2d0968 version bump to 0.6.9
* fix i18n
2014-12-20 23:38:37 +01:00
Ricki Hirner 45464be2f2 shrink bytecode with ProGuard for both debug/release builds (closes #202)
* enable ProGuard for debug builds because real-life testing is done with debug builds
2014-12-20 23:34:32 +01:00
Ricki Hirner e6573ed4c1 Re-enable tests again 2014-12-20 23:33:02 +01:00
Ricki Hirner e7f610c150 Remove Debug settings activity
* remove debug settings activity; logging can now be enabled via adb setprop, since that's the default way of enabling verbose logs in HttpClient's Android flavour and adb is required for viewing the logs anway
* update i18n accordingly
2014-12-20 21:35:52 +01:00
Ricki Hirner 59d3f76f0c Merge with origin/master 2014-12-20 21:15:08 +01:00
Ricki Hirner 62fa9055d0 Merge remote-tracking branch 'origin/master' into android-studio
Conflicts:
	.gitignore
2014-12-20 20:40:28 +01:00
Ricki Hirner 4a3c910506 Migrated to Android Studio/gradle
* moved all dependencies to gradle instead of shipping .jar files in the app/lib directory
* switched to official Android port of HttpClient instead of httpclientandroidlib
* new .gitignore and project files
2014-12-20 20:21:46 +01:00
Ricki Hirner f41c0d36cc Fiddling around with UI again and again and again … hoping for console-only mobile phones one day (fixes #386) 2014-12-07 12:58:50 +01:00
Ricki Hirner acddc6d0af Use https links for davdroid.bitfire.at 2014-12-05 21:26:34 +01:00
Ricki Hirner 864a5732cb Mark as compatible with Android 5.0 2014-12-01 01:17:01 +01:00
Ricki Hirner 1143341600 Merge branch 'master' of github.com:bitfireAT/davdroid 2014-11-30 21:07:49 +01:00
Ricki Hirner aadff35f00 Various bug fixes
* don't set left/right padding in list items (fixes #240)
* don't allow list headers to be selected (part of issue #355)
* version bump to 0.6.8
2014-11-30 21:04:59 +01:00
Ricki Hirner 28bf93d453 Bug fixes
* fix a bug in handling TXT records (hopefully fixes #383)
* fix invalid translation strings
2014-11-30 18:09:31 +01:00
Ricki Hirner 6a96af6dbb Bug fixes
* fix a bug in handling TXT records (hopefully fixes #383)
* fix invalid translation strings
2014-11-29 22:52:51 +01:00
Ricki Hirner cc89028b42 Merge pull request #376 from pokoli/update_catalan_translation
Update catalan translation
2014-11-22 22:20:40 +01:00
Sergi Almacellas Abellana f9a092ceb8 Update catalan translation 2014-11-22 18:20:19 +01:00
Ricki Hirner 333bf99147 Merge pull request #375 from gjtoth/master
Updated Hungarian translation.
2014-11-22 11:51:46 +01:00
jtg 62ad7431df Updated Hungarian translation. 2014-11-22 00:54:32 +01:00
Ricki Hirner e48cb8d546 Merge pull request #372 from svetlemodry/master
Updated Czech translation
2014-11-19 13:03:12 +01:00
Jaroslav Lichtblau 07c0474874 Updated Czech translation 2014-11-18 20:50:22 +01:00
Ricki Hirner 4ffe2b7c48 Improve handling of unknown timezone definitions (should fix #333) 2014-11-15 13:49:38 +01:00
Ricki Hirner 2739a05fac Don't require # for calendar colors (see #136, closes #238) 2014-11-15 12:58:38 +01:00
Ricki Hirner 3dfcffc015 Merge pull request #364 from phy25/values-zh-CN
Updated the Chinese Simplified translations
2014-11-14 20:39:19 +01:00
Ricki Hirner 9541a05f3a Merge pull request #365 from pejakm/srupd
Updated Serbian translations
2014-11-14 20:39:09 +01:00
Mladen Pejaković 7087b4f1df Oops... 2014-11-14 19:59:11 +01:00
Mladen Pejaković 9867904de3 [Translations] Update Serbian 2014-11-14 19:54:55 +01:00
phy25 a9f49f8ced Updated the Chinese Simplified translations according to the simplified strings.xml. 2014-11-14 01:02:19 +08:00
Ricki Hirner 4594f35065 Minor changes
* new icons with 3D effect as required in Android Iconography Guide
* target SDK version updated to 20
* replaced UI directions left/right by start/end for RTL support
* Show "unnamed calendar"/"unnamed address book" instead of "null" in "Select collections" fragment
* "Add account" button now directly adds DAVdroid account
* .svgz for "How DAVdroid interacts with other components" added to doc
2014-11-12 14:37:22 +01:00
Ricki Hirner f415c6b082 Various fixes
* fix minor translation issue that caused DAVdroid to crash when showing an I/O error
* don't select TLS ciphers for Android 5.0+ (it has more secure default settings); closes #344
2014-11-09 19:58:07 +01:00
Ricki Hirner 8deb5fd864 Add license info for dnsjava and some copyright notes 2014-11-08 17:57:14 +01:00
Ricki Hirner fb3225d92a Merge branch 'master' of github.com:rfc2822/davdroid
Conflicts:
	res/values-hu/strings.xml
2014-11-08 17:38:27 +01:00
Ricki Hirner ce39581136 Version bump to 0.6.7
* a few fixes for SRV service detection
* localization strings cleanup
* new policy for localization: translators are only mentioned on their own translation from now on
2014-11-08 17:32:54 +01:00
Ricki Hirner 3af75a2baa CalDAV/CardDAV service discovery with SRV/TXT records
* Structural changes in the strings file (for translations)
2014-11-08 16:33:34 +01:00
Ricki Hirner fe173e6041 Merge pull request #354 from gjtoth/master
Refined Hungarian translation.
2014-11-05 20:34:50 +01:00
jtg e517899818 Refined Hungarian translation. 2014-11-05 15:44:50 +01:00
Ricki Hirner 2560ec4cab Initial support for SRV/TXT service discovery 2014-11-04 23:04:24 +01:00
Ricki Hirner 5a1dbcd860 Merge pull request #347 from gjtoth/master
Attribution for translations added to the Hungarian version.
2014-11-04 09:06:25 +01:00
Ricki Hirner fa4ef5d81a Version bump to 0.6.6 2014-11-03 20:24:10 +01:00
Ricki Hirner 2b891d2887 Show Website icon in main activity only if there's enough room 2014-11-03 19:39:55 +01:00
Ricki Hirner 6bd8214cca Messing around with trailing slash again and again + tests (fixes #349) 2014-11-03 19:30:07 +01:00
jtg 014cd5d431 Merge remote-tracking branch 'upstream/master' 2014-11-01 21:37:28 +01:00
jtg f2395c07cd Attribution for translations added. 2014-11-01 21:31:11 +01:00
Ricki Hirner 6e7d3dba37 Merge pull request #346 from gjtoth/master
Hungarian translation added. Thanks to @gjtoth
2014-11-01 21:06:38 +01:00
jtg 3508aad847 Hungarian translation added. 2014-11-01 19:58:34 +01:00
Ricki Hirner 1dbb67fbbe Mention Chinese translator 2014-10-31 17:36:59 +01:00
Ricki Hirner 55731923ef Ensure trailing slash on user-given base URL (assuming users will always provide collections or base paths) 2014-10-31 17:32:16 +01:00
Ricki Hirner c5cc468ed4 Set reasonable TLS settings
* disallow SSLv3
* allow more secure ciphers (closes #344)
* version bump to 0.6.5
2014-10-31 16:56:30 +01:00
Ricki Hirner 9d60228c0e Add TLS cipher info to tests (see #344) 2014-10-31 15:39:12 +01:00
Ricki Hirner 3bd94ea577 Ensure trailing slashes are always used for collections + tests 2014-10-31 15:29:00 +01:00
Ricki Hirner d5326e31bd Merge branch 'master' of github.com:rfc2822/davdroid 2014-10-27 15:18:58 +01:00
Ricki Hirner 1c4b51b15a Use new ical4j version to fix timezone bug
* use ical4j 1.0.x-maintenance (self-compiled 27 Oct 2014)
* upstream fixes timezone bug (fixes #186)
* try to turn off the check for time zone updates because it's unnecessary in most cases (fixes #239)
* mention ical4j and its major version in generated iCalendars
* update to ez-vcard 0.9.6 from upstream
* version bump to 0.6.4
2014-10-27 15:14:49 +01:00
Ricki Hirner 326ee9033d Merge pull request #338 from phy25/values-zh-CN
Add Chinese Simplified translation. Thanks to @phy25
2014-10-18 15:19:47 +02:00
phy25 3ef1193d42 Added Chinese Simplified translation. 2014-10-18 19:41:15 +08:00
Ricki Hirner 65790e5d0e Allow adb backups (not Google cloud backups!) for DAVdroid because of user request 2014-10-17 15:33:00 +02:00
Ricki Hirner dc1a5d70d2 Merge branch 'master' of github.com:rfc2822/davdroid 2014-10-10 13:00:36 +02:00
Ricki Hirner 7d8acef77d Send appropriate Accept/Content-Type headers + tests (closes #328) 2014-10-10 12:59:53 +02:00
Ricki Hirner f5801ee797 Merge pull request #329 from pejakm/srupd
Serbian translation fixes and updates
2014-10-07 10:02:59 +02:00
Mladen Pejaković 48bb3c1e31 Serbian translation fixes and updates 2014-10-03 20:30:20 +02:00
Ricki Hirner ee9735472b Setup: always show 401 Unauthorized errors
* also increase version code and version to 0.6.3
2014-10-02 17:05:20 +02:00
Ricki Hirner 1a1a81cd9e Version bump to 0.6.3
Add translator info
2014-10-02 15:37:36 +02:00
Ricki Hirner 2363ed12c8 Don't fail to detect resources if principal is not available for CalDAV/CardDAV 2014-10-02 12:04:29 +02:00
Ricki Hirner deca6bb955 Merge pull request #318 from svetlemodry/master
Czech translation – thanks to @svetlemodry
2014-09-30 18:07:50 +02:00
Ricki Hirner 7d382bfd65 Merge pull request #324 from pejakm/srtr
[Translations] Add Serbian – thanks to @srtr
2014-09-30 18:07:17 +02:00
Mladen Pejaković 2e5ea2c2c6 Serbian translation fixes 2014-09-25 22:19:54 +02:00
Mladen Pejaković c05b0161d2 [Translations] Add Serbian 2014-09-25 16:30:08 +02:00
Jaroslav Lichtblau a7734b3d83 typo/missing string fix 2014-09-11 12:48:51 +02:00
Jaroslav Lichtblau e332b3a8c6 Czech translation file
czech translation of davdroid text strings
2014-09-11 12:44:07 +02:00
Ricki Hirner 0a3578f3da Set STARRED as integer, not boolean (should fix #294) 2014-08-10 18:56:19 +02:00
Ricki Hirner cdbd588d60 Version bump to 0.6.2 2014-08-09 10:29:09 +02:00
Ricki Hirner b353b74041 Don't require capabilities on home sets that MAY not be available on these collections (fixes #289) 2014-07-31 14:00:36 +02:00
Ricki Hirner 7e04c4b88a VCard 4 support detection; new ez-vcard version
* detect CardDAV VCard version support using supported-address-data + test
* account setting for supported VCard version
* don't ask for calendar details when querying CardDAV collections
* don't ask for address book details when querying CalDAV collections
* ez-vcard update to 0.9.5 (fixes #268), adapted exception handling
* refactoring: unnecessary DavProp prefixes removed
2014-07-30 01:47:35 +02:00
Ricki Hirner 89624c6b9c Move resource detection to separate class + tests 2014-07-27 13:01:54 +02:00
Ricki Hirner 126b89beb5 Don't check capabilities on principal URL but on calendar/address book home set (fixes #286) 2014-07-25 00:04:35 +02:00
Ricki Hirner c83588d736 Version bump to 0.6.1; show exceptions from resource detection (see #284, see #286) 2014-07-24 23:12:48 +02:00
Ricki Hirner 10a1374492 Add group support via VCard CATEGORIES (closes #48) 2014-07-24 23:12:43 +02:00
Ricki Hirner d630954cb1 Handle redirections to relative URLs correctly (see #282) + tests; minor GUI change 2014-07-20 16:08:02 +02:00
Ricki Hirner 0381bd4aae Version bump to 0.6 (drop -alpha suffix) 2014-07-19 00:44:28 +02:00
Ricki Hirner 2b4f9d478d Add support for well-known URIs (RFC 5785) - closes #148 2014-07-19 00:39:07 +02:00
Ricki Hirner 18f36f0257 Handle HTTP redirections (fixes #83) 2014-07-18 22:31:45 +02:00
Ricki Hirner ba8275a6cf Don't crash when Calendar Provider is not available at account creation
(fixes #276)
2014-07-12 13:24:49 +02:00
Ricki Hirner c7d26ecd74 Allow only one anniversary to comply with VCard 4 (fixes #277) 2014-07-12 13:08:00 +02:00
Ricki Hirner 262a7a701a Version bump to 0.5.14-alpha
* added some documentation (JavaDoc)
* CTags are not cached in LocalCollection anymore
* added CTag test for LocalCalendar
2014-05-24 13:40:14 +02:00
Ricki Hirner 5ed506d434 Various upload changes
* fix issue when first upload fails (fixes #233)
* show debug settings menu item in main activity
* update ETag in local database when a PUT request returns an updated ETag, meaning that
  locally created/updated records won't be downloaded from the server immediately after the upload
2014-05-23 13:08:08 +02:00
Ricki Hirner c1882ac189 Better invalid VCard handling
* ignore unknown properties which have invalid syntax instead of crashing
* log validation warnings for created VCards
* add VCard tests
2014-05-09 16:54:02 +02:00
Ricki Hirner 7d56dee43c Ignore crappy unknown properties with illegal syntax (see issue #118) 2014-05-09 15:55:05 +02:00
Ricki Hirner ecf842e8cd Debugging improvements
* force debugging options for resource detection (because there's no way to turn them on until there's an account)
* provide "report issue" button in debug settings
* provide CONTRIBUTING.md, including instructions on how to report issues
* increase socket timeout from 20 to 45 seconds (hopefully fixes #237)
* previous commit: increase total simultaneous connections to 3 (2 per host)
2014-05-09 15:44:38 +02:00
Ricki Hirner 9d2124ca3a Debug settings
* preference screen for debug settings
* allow to disable HTTP compression
* verbose network logging only when activated
* single HttpClient for all threads (before: single HttpClient per adapter)
* bump version to 0.5.13-alpha
2014-05-08 21:22:52 +02:00
Ricki Hirner 6a23d4ac56 Make server URL, user name and password horizontally scrollable (fixes
#241)
2014-05-08 15:57:43 +02:00
Ricki Hirner e40b997a8b Support HTTP(S) proxies (closes #232) 2014-05-07 22:53:22 +02:00
Ricki Hirner d96c3eb86c Enable all supported TLS protocols (enables TLSv1.1/1.2 if supported by Android version on device, see #229) 2014-04-06 17:54:27 +02:00
Ricki Hirner 3bda992fad Version bump to 0.5.12 2014-04-05 12:46:00 +02:00
Ricki Hirner d1c9be0ba0 Read non-high-res photos, too (fixes #53 again); remove stale connection check because RetryStrategy should be enough 2014-04-05 12:37:46 +02:00
Ricki Hirner fe9945fdd2 don't close httpClient while syncing 2014-04-04 21:18:11 +02:00
Ricki Hirner 758a704dd2 Retain unknown VCard properties, ez-vcard update, handle stale connections
* store unknown VCard properties in an extra column and load them when generating a new VCard (closes #118)
* upgrade to ez-vcard/0.9.3 (fixes sync error reported via Play Store)
* (re-)enable stale collection check, RetryHandler to retry idempotent CalDAV/CardDAV requests (hopefully fixes #225)
* always set FN/display name (take organization if no structured name is available) (hopefully fixes #227)
2014-04-04 20:47:46 +02:00
Ricki Hirner 7945116877 Version bump to 0.5.11-alpha 2014-03-29 22:42:53 +01:00
Ricki Hirner 02dec1507a SNI support for Android <4.2, too (see issue #9) 2014-03-29 17:03:29 +01:00
Ricki Hirner 5a1cc2d85f Merge pull request #214 from pokoli/catalan_translation
Escape reserved caracters on catalan translation to do not break the build
2014-03-26 22:41:54 +01:00
Sergi Almacellas Abellana 244c4defbb Escape reserved caracters on catalan translation to do not break the build 2014-03-26 20:58:33 +01:00
Ricki Hirner d0f5ad8c91 Merge pull request #213 from pokoli/catalan_translation
Add catalan translations – thanks @pokoli
2014-03-26 19:59:12 +01:00
Sergi Almacellas Abellana dab0f0d40e Add catalan translations 2014-03-26 19:50:41 +01:00
Ricki Hirner 268cae690a Version bump to 0.5.10.2-alpha 2014-03-25 19:17:12 +01:00
Ricki Hirner f830b7eb86 Multiple bug fixes
* doesn't try to update address book URL when there is no sync-able address book (fixes #208)
* add test for HTTPS/SNI
2014-03-25 19:12:59 +01:00
Ricki Hirner fb5a23a3cc Merge pull request #210 from bubak4/patch-1
Fix #207 (regression: http not supported). Thanks @bubak4
2014-03-25 13:51:42 +01:00
bubak4 973abcf315 Fix #207 (http not supported #207)
Re-register http factory, as it was removed by accident during refactoring.
2014-03-25 11:52:23 +01:00
Ricki Hirner bde87cbb74 Add SNI support for Android 4.2+ for new API again (fixes #205) 2014-03-23 22:19:59 +01:00
Ricki Hirner 367de865e3 Adapted tests 2014-03-23 13:01:46 +01:00
Ricki Hirner 1b5c27537e Upgrade to HttpComponents 4.3.2, version bump to 0.5.10 2014-03-23 01:12:32 +01:00
Ricki Hirner b0892c29be New settings management
* Account settings are now read/set using AccountSettings
* AccountSettings upgrades old account settings when necessary
* Settings v1: use absolute URLs instead of paths and discard principal path
  (required for future implementation of well-known URIs)
2014-03-16 20:38:53 +01:00
Ricki Hirner 2c3d8c8846 Show JB Workaround info when installed from Google Play 2014-03-16 16:07:40 +01:00
Ricki Hirner f134246ddf Check base URL validity with new URI() instead of new URL() (fixes #184) 2014-03-15 13:39:02 +01:00
Ricki Hirner 2160ea51dd Correct icon sizes to match Android iconography; translations 2014-03-15 12:32:45 +01:00
Ricki Hirner c44097eda8 Fix a bug that deletes entities remotely even if they have changed remotely (closes #197) 2014-03-14 23:14:32 +01:00
Ricki Hirner 4994df7d06 Version bump to 0.5.9
* GUI changes for read-only collections (new icons)
* require only bind, unbind and write-content privileges
2014-03-09 17:55:16 +01:00
Ricki Hirner fe4ceaf6a3 Support for read-only calendars (closes #126)
* relevant RFCs go into the doc/ directory for reference purposes
* read-only calendar collections are set as read-only in Android
* HTTP exception refactoring to mark 4xx HTTP errors as hard sync errors (numAuthExcetions/numParseExceptions) for Android sync manager
* query current-user-privilege-set for resources, detect read-only resources
* show read-only resources as read-only in SelectCollectionsFragment
* minor refactoring (DavProp.*)
2014-03-09 15:12:59 +01:00
Magnus Anderssen 00ca7eb043 French translation that compiles 2014-02-27 15:57:01 +01:00
unknown bf694f2300 French translation 2014-02-27 15:57:01 +01:00
xphnx 5c1f61456c Update strings.xml 2014-02-27 15:57:00 +01:00
xphnx 3962cf1d59 Update strings.xml 2014-02-27 15:57:00 +01:00
Ricki Hirner 4cea0cd585 Bump version to 0.5.8.1, ignore VCards without VEVENTs (fixes #178) 2014-02-27 15:56:56 +01:00
Ricki Hirner 32ced2ceff Version bump to 0.5.8-alpha
* version bump
* introduction of LocalCalendarTest
* always provide DTEND and not DURATION for all-day events (compatibility)
* ez-vcard 0.9.2
2014-02-09 00:56:00 +01:00
Ricki Hirner e59e6b9e01 Better DTSTART/DTEND handling
* generalized InvalidResourceException for parsing errors
* only iCals with both DtStart and DtEnd/Duration are processed (DtEnd will be derived by iCal4j when not present in .ics)
* all-day events must last at least one day (fixes #166)
* other DtEnd/Duration rewriting + tests
2014-02-08 18:53:31 +01:00
Ricki Hirner a8f60eb8e5 0.5.7-alpha1: crash bug fix 2014-02-02 19:21:51 +01:00
Ricki Hirner 95c1dd8770 Merge pull request #165 from xphnx/patch-1
Spanish translation
2014-02-02 19:21:02 +01:00
xphnx 43ccb3eb6f Update and rename strings.xml to strings-es.xml
Spanish translation
2014-01-28 18:47:01 +01:00
Ricki Hirner 6b10b47b70 Fix crash when ROLE/TITLE but no ORG value set (fixes #163) 2014-01-26 12:50:50 +01:00
Ricki Hirner 11eed4d80b Better organization, job title/description handling
* handle VCard structured organization (department), job title/description correctly
* there's still an EZVCard bug: https://code.google.com/p/ez-vcard/issues/detail?id=13
* bump version code
2014-01-25 13:07:33 +01:00
Ricki Hirner 9ee8ec8144 Version bump to 0.5.7
* use VCard ROLE as "department" and TITLE as "job title"
* version bump to 0.5.7
2014-01-25 11:47:36 +01:00
Ricki Hirner 8b8d4947da Fix regression (8a651f135b) causing unnecessary traffic (closes #162, closes #147) 2014-01-25 11:11:48 +01:00
Ricki Hirner 3384cec346 Minor changes
* improved logging
* allow installation to internal storage only (informational change, doesn't do anything actually, closes #160)
2014-01-21 23:11:17 +01:00
Ricki Hirner 31d788ff7a Minor bug fixes
* work-around for crash when remote ETag is empty (shouldn't happen but happens sometimes)
* fetch multiple contact URLs (not only the first one) from contacts provider (fixes #150)
2014-01-12 20:07:26 +01:00
Ricki Hirner 2d40335cb9 0.5.6α Wage Slave
* support primary (preferred) phone numbers/email addresses
* fix (crash) bug in URI sanitation
* don't set calendar properties not supported by device's Android version
* require already-set remote file name when finding updated records
* version bump to 0.5.6α
2014-01-06 20:03:59 +01:00
Ricki Hirner ad5f8c1d7c Version bump to 0.5.5-alpha 2014-01-06 05:48:56 +01:00
Ricki Hirner 6b8ff66a21 Commit after every full record insertion/update (closes #135)
Docs say that TransactionTooLargeException is thrown when the buffer reaches 1 MB. Because we can't know
how large a single insertion/update is and there may be contacts with larger photos, it's only safe to
commit after every insertion/update. If photos exceed ~ 900 kB it would be required to write the photos
to an asset file (maybe I'll do this later, then we can group insertions/updates again).
2014-01-06 05:45:31 +01:00
Ricki Hirner 60026d8ade Bug fixes
* optimise URL repairs: don't replace "[", "]", ":" in host names, allowing IP address literals (closes #98)
* SyncManager: minor refactoring
2014-01-06 05:28:28 +01:00
Ricki Hirner de9872da7e Use (final) static wherever possible for method signature clarity/performance 2013-12-31 19:13:22 +01:00
Ricki Hirner a283b18e91 Allow vCard4 fields (ANNIVERSARY) in vCard3, too (closes #131). 2013-12-31 16:55:29 +01:00
Ricki Hirner 63be212396 Minor refactoring
* update target SDK version to API level 19
* less null return values
* explicit Resource generateUID/generateName methods
* use StringUtils when it makes sense
2013-12-31 16:37:31 +01:00
Ricki Hirner b69855e61e Support for SIP addresses (closes #78)
* support for SIP addresses (always exported as IMPP, imported as IMPP/X-SIP)
* version bump to 0.5.4
2013-12-26 17:23:14 +01:00
Ricki Hirner 472760ead4 Minor improvements
* generalise davdroid.ArrayUtils.partition; tests
* lower multi-get limit to 35
2013-12-25 15:15:23 +01:00
Ricki Hirner 68e5525e1f Fetch remote records in chunks of max. 50 to avoid server and memory problems
* performance optimisation
* refactoring
2013-12-24 12:49:23 +01:00
Ricki Hirner 38ae48e685 Fetch resources for uploading one-by-one to save memory 2013-12-24 12:49:18 +01:00
Ricki Hirner ee8611f90b Refactoring
* Contacts/Event.toEntity() return ByteArrayOutputStream instead of String
* throw DavNoContentException instead of returning null to avoid NullPointerExceptions
* always close input/output streams
* always call consumeContent() for HTTP response entities to avoid memory leaks
2013-12-22 00:58:58 +01:00
Ricki Hirner 3bdbf37608 throw RecordNotFoundExceptions only when specific records are requested 2013-12-21 20:54:54 +01:00
Ricki Hirner f72f0f3b91 Refactoring
* add overflow marker "…" to LoggingInputStream
* split the populate() methods
* always close cursors (resolves #120)
* introduce LocalStorageException and its subclass RecordNotFoundException and catch RemoteException earlier
* split synchronize() method into phases
* commit after every added/modified record (obsoletes MAX_UPDATES_BEFORE_COMMIT)
* read contact photos from (high-res) asset instead of the thumbnail blob (resolves #121)
2013-12-20 22:42:18 +01:00
Ricki Hirner c01d969e21 Version bump to 0.5.3-alpha
* add null checks (should fix #117)
* more detailed DAV exceptions for error handling
* better logging (limited to 10 kB per log entry) to avoid memory problems
* DavMultiget creates requests itself (instead of WebDavResource)
2013-12-15 19:50:20 +01:00
Ricki Hirner 0256c50694 Version bump to 0.5.2-alpha 2013-12-11 17:08:10 +01:00
Ricki Hirner 7bc145ca47 avoid NullPointerExceptions when contact data rows with custom types have no LABEL 2013-12-11 15:48:21 +01:00
Ricki Hirner 6056bab6e2 Amend EditSchema and VCard fields
* write PRODID with DAVdroid and ez-vcard version to VCards
* remove limit of 1 for contact Web sites in contact EditSchema
* add organization to contact EditSchema
* don't set null values in ORG/ROLE
2013-12-11 15:19:50 +01:00
Ricki Hirner fa675658a8 More input checks
* check Android's DURATION, RRULE, RDATE, EXRULE, EXDATE values for empty strings before processing
* catch InvalidArgumentException on invalid RRULE values from Android
* add Depth: 0 to REPORT multi-get requests as required by CalDAV/CardDAV RFCs
* fix crash bug on multi-get responses without content (see issue #113)
2013-12-11 14:48:05 +01:00
Ricki Hirner 4f722ee392 Version bump to 0.5.1; fix crash bug (fixes #108) 2013-12-08 14:00:48 +01:00
Ricki Hirner 3066856e09 Ignore invalid IM addresses (fixes #105), Lombok 1.12.2 2013-12-08 13:31:59 +01:00
Ricki Hirner 14784504c8 Version bump to 0.5
* don't download contact photos from URLs (we only want thumbs anyway)
* always use yyyy-MM-dd for birthdays/anniversaries
2013-12-06 20:47:22 +01:00
Ricki Hirner 723fd58285 Add more vCard fields
* support for vCard organization (company / job title)
* support for vCard IMPP addresses (closes #105)
* refactoring
2013-12-06 14:19:00 +01:00
Ricki Hirner 3f10968228 VCard 3 support
* switch to ez-vcard instead if iCal4j-vcard
* generate VCard 3 by default (fixes #67)
* support for more phone number/email/address types, including "custom" types
* limit to 1 URL and 1 note (Android limit)
* support for anniversaries
2013-12-04 14:04:18 +01:00
Ricki Hirner b5cd37daed Fix crash bug when ORGANIZER has no URI scheme 2013-12-03 20:34:50 +01:00
Ricki Hirner effdc7fee4 Version 0.4.4
* allow to enter account name at the end of account setup (fixes #13)
* advise user to enter email address as account name (closes #24)
* support for iCal TRANSP (availability free/busy field)
* create better UIDs using iCal4j UidGenerator and Settings.Secure.ANDROID_ID
* better ORGANIZER/ATTENDEE support, but there are big Android issues yet
* various improvements and bug fixes
2013-12-01 16:08:36 +01:00
Ricki Hirner 1409da386e Provide DTEND for single-time events and DURATION for recurring events to Android calendar data provider (fixes #60) 2013-11-30 00:50:13 +01:00
Ricki Hirner 5528d19257 CalDAV: ignore collections without VEVENT support (for instance, VTODO collections) 2013-11-30 00:06:16 +01:00
Ricki Hirner b5f7597c38 add DURATION iCal->Android, collection refactoring 2013-11-26 22:20:00 +01:00
Ricki Hirner 48011928a3 version bump to 0.4.3-alpha 2013-11-24 13:21:59 +01:00
Ricki Hirner 62a066b55d treat 404 on DELETE as success (fixes #94) 2013-11-24 13:20:44 +01:00
Ricki Hirner 2039071c77 support for reminders; better support for recurring events; bug fixes
* use DURATION instead of DTEND when required (fixes #60)
* send VTIMEZONEs along with VEVENTs (fixes #66)
* support for reminders (closes #70)
* bug fixes (UID parsing)
2013-11-24 13:15:31 +01:00
Ricki Hirner 5995d3d319 security fix: validate certificates and not only host names (thanks Günter) 2013-11-24 03:07:50 +01:00
Ricki Hirner b14bd337de version bump to 0.4.2
* imports calendar default time zone on account creation
2013-11-22 15:11:39 +01:00
Ricki Hirner 70075f9f09 Bug fixes, SDK level 19
* use SDK level 19
* add action to all preference intents (fixes #92)
* use StrictHostnameVerifier instead of HttpsURLConnection.getDefaultHostnameVerifier() (fixes #88)
* use per-WebDavResource instead of static DavHttpClient to enable different settings for different accounts
2013-11-22 15:11:08 +01:00
Ricki Hirner 15ff7dfa18 Bug fixes, refactoring, version bump
* don't manually decode URLs for comparing (because we now always use encoded URLs)
* .ics: VERSION before PRODID
* refactoring: move WebDavResource.httpClient to new singleton DavHttpClient
* close input streams in propfind/multi-get
* version bumpt to 0.4.1
2013-11-17 15:49:10 +01:00
Ricki Hirner 7c19e1ac5b Fix invalid URLs one more time 2013-11-17 13:04:57 +01:00
Ricki Hirner 190e3f766f Preserve order in multiget requests (thanks Marten, fixes #65) 2013-11-15 11:21:03 +01:00
Ricki Hirner e95a5ce742 SNI support (fixes #9); only available for API level >= 17 (Android 4.2+) 2013-11-15 01:05:18 +01:00
Ricki Hirner 43e723dd5f Merge pull request #74 from oliver/master
fix NumberFormatException when parsing "ff" alpha value into a Byte
2013-11-14 01:41:46 -08:00
Oliver Gerlich 31bdfbe690 fix NumberFormatException when parsing "ff" alpha value into a Byte 2013-11-13 22:09:14 +01:00
Ricki Hirner 03290e2653 import Apple calendar-color with r,g,b,a (see issue #31) 2013-11-12 22:17:46 +01:00
Ricki Hirner 09e36b9b6f don't decode URI pathes (fixes #58), don't expect GET/POST/PUT on collections 2013-11-12 18:39:05 +01:00
Ricki Hirner c69212957a Version bump to 0.3.8 (version code 12) 2013-11-09 12:45:09 +01:00
Ricki Hirner 91691218b9 Importing calendar colors from <calendar:color xmlns="http://apple.com/ns/ical/"> element (see #31) 2013-11-09 12:43:21 +01:00
Ricki Hirner e5e9e0511c avoid NullPointerException (hint from ANR) 2013-11-09 10:54:29 +01:00
Ricki Hirner 25923cf3b2 Always append trailing slash to address-book/calendar home set to avoid redirections 2013-11-09 01:36:09 +01:00
Ricki Hirner 29b4087278 RoboHydra tests, URL sanitizing
* RoboHydra tests
* intelligent URL sanitizing (fixes #58, fixes #49, see issue #11, should fix #45)
2013-11-09 01:13:38 +01:00
Ricki Hirner 4ac38a9eb7 refactoring, RoboHydra tests 2013-11-09 01:04:35 +01:00
Ricki Hirner d48bb41003 basic WebDAV tests 2013-11-03 01:40:24 +01:00
Ricki Hirner 6039f884e3 Version bump to 0.3.7 (version code 11) 2013-11-02 16:18:17 +01:00
Ricki Hirner 0b291381e6 support for structured addresses (closes #29); bug fixes 2013-11-02 16:09:48 +01:00
Ricki Hirner ceebea5d1d iCal4j update, handle relative URLs with colons
* iCal4j 1.0.5 -> 1.0.5.2, iCal4j-vcard 0.9.5 -> 0.9.6.2
* handle relative URLs with colons in name (see issue 45)
2013-11-01 15:27:48 +01:00
Ricki Hirner a162eb664c improved photo parsing, DELETE statement
* let ical4l-vcard do the photo parsing; tested with posteo, OwnCloud,
eM Client; fixes #53
* improved DELETE statement to avoid
"android.database.sqlite.SQLiteException: Expression tree is too large"
(fixes #54)
* added "file name required" validation
2013-11-01 12:13:45 +01:00
Ricki Hirner 6dbe2cff9b Version bump to 0.3.6
* show app version in main activity
2013-10-30 17:27:44 +01:00
Ricki Hirner b0f6eab402 don't follow redirects (see issue #52) 2013-10-30 17:20:04 +01:00
Ricki Hirner 5065a5c06a better vcard parsing error logging, always append trailing slash to collection URLs 2013-10-30 15:14:19 +01:00
Ricki Hirner db32d909b0 Main activity, minor changes
* Main activity with description and links
* Decrease MAX_UPDATES_BEFORE_COMMIT to 25 (may fix #25)
2013-10-30 12:17:26 +01:00
Ricki Hirner 15886746a8 version 0.3.5-alpha
enhancements:
- don't show calendars/address books in account when no calendars/address books were added

bug fixes:
- select only events from correct calendar when querying (fixes #37)
- ignore non-OK values in multi-status response (fixes #35)
- fix wrong entries in setup calendar/addressbook selection
- commit after every 100 resource downloads to avoid TransactionTooLargeException
- don't delete resources without file name (fixes #33)
- avoid crashes when content providers return null cursors
- ignore invalid URLs (for instance, with spaces) in WebDAV multi-status responses
2013-10-27 01:38:47 +02:00
Ricki Hirner fac5767730 don't show calendars/address books as syncable in account when no calendars/address books were added; select only events from correct calendar when querying (fixes #37) 2013-10-27 01:20:57 +02:00
Ricki Hirner 3b4ef177b3 Don't delete resources without file name (fixes #33) 2013-10-27 00:14:55 +02:00
Ricki Hirner 68ccdca54f Use correct caching for address book/calendar selection ListView 2013-10-27 00:03:41 +02:00
Ricki Hirner d8699baa16 Bug fixes
* ignore non-OK values in multi-status response (fixes #35)
* fixes wrong entries in setup calendar/addressbook selection
2013-10-26 23:45:10 +02:00
Ricki Hirner 07f668fde6 Commit after every 100 resource downloads to avoid TransactionTooLargeException 2013-10-26 22:37:45 +02:00
Ricki Hirner a65f821802 bug fixes
* avoid crashes when content providers return null cursors
* ignore invalid URLs (for instance, with spaces) in WebDAV multi-status responses
2013-10-26 20:35:22 +02:00
Ricki Hirner 2b02439721 Fix "can't edit contact" / "account doesn't show up" and other bugs; refactoring
* support for phonetic names (closes #19)
* update contacts.xml, tested with 4.0 (Samsung), 4.2 (Cyanogen), 4.3 (Cyanogen) (fixes #5, fixes #6, fixes #7)
* smarter error handling (1): notify sync manager in case of HTTP auth errors
* smarter error handling (2): just ignore the dubious resources instead of notifying Android sync service
* refactoring: created DavSyncAdapter and move common code to it
* version bump to 0.3.4-alpha
2013-10-20 17:40:03 +02:00
Ricki Hirner f0f767718c fix potential crash bug when server returns no collection members 2013-10-16 18:22:59 +02:00
Ricki Hirner 844c63e8dc version bump to 0.3.3-alpha 2013-10-16 00:18:00 +02:00
Ricki Hirner f57e54803d fixes issue #11 2013-10-16 00:09:29 +02:00
Ricki Hirner 39f3e43890 add contacts.xml and birthday (couldn't test it) 2013-10-15 23:06:01 +02:00
Ricki Hirner 4ed3502286 disable strict parsing of XML response for PROPFIND and multiget 2013-10-15 23:06:01 +02:00
Ricki Hirner cefaffa650 Ant build support
* build.xml for Ant
* necessary library re-organization
* compatiblity changes to code
* now buildable via Ant and Eclipse ADT
* eclipse-libs has to be included for ADT building only
* ready for Fdroid
2013-10-12 14:55:49 +02:00
Ricki Hirner 7985ba2f0a various improvements
* fixed annoying bug where all imported contacts where starred
* checkbox for preemptive authentication in setup
* support for (non-preemptive) Digest authentication
* add UID when received entities don't contain one
* version bump to 0.3.2-alpha
* build.xml for ant/Fdroid
2013-10-12 13:38:13 +02:00
Ricki Hirner dd03b0450d fix annoying bug that marked contacts as starred 2013-10-11 21:50:22 +02:00
Ricki Hirner f1661fffac bug fixes
* added server path to account name to allow Baïkal CalDAV+CardDAV setup as two accounts
* enabled relaxed iCal parsing/unfolding for better compatibility
* fixed crash bug for .ics files that don't contain events
* fixed bug where all events where deleted locally when using multiple calendars
2013-10-09 21:59:56 +02:00
Ricki Hirner a251973761 Bug fixes:
* calendar multi-get
* debug output for propfind/multiget requests
* setup GUI crash if no address book/calendar available
2013-10-09 14:43:28 +02:00
Ricki Hirner 4fb94a89df refactoring
* ical4j-vcard instead of ez-vcard
* removed Guava dependencies in favor of Apache Commons IO
2013-10-06 13:48:53 +02:00
Ricki Hirner abd67609e6 Refactoring
* move common code to super-classes
* new icons
2013-10-05 20:34:26 +02:00
Ricki Hirner 7b262721e6 Calendar improvements
* recurring events
* classification
* status
* attendees
* ical4j 1.0.4 -> 1.0.5
* remove biweekly (replaced by ical4j)
2013-10-05 10:59:19 +02:00
Ricki Hirner 6f12a19b14 biweekly 0.3 -> 0.3.1, incomplete time zone support for events, Android-compatible Lombok 2013-09-27 18:19:54 +02:00
Ricki Hirner 1bc72f340f new Android test project 2013-09-18 21:11:42 +02:00
Ricki Hirner f724a89bd8 first commit 2013-09-18 20:03:36 +02:00
427 changed files with 27840 additions and 12779 deletions

No files matched your search

+15 -4
View File
@@ -1,20 +1,31 @@
image: registry.gitlab.com/bitfireat/davdroid:latest
image: registry.gitlab.com/bitfireat/docker-android-emulator:latest
before_script:
- git submodule update --init --recursive
- export GRADLE_USER_HOME=`pwd`/.gradle; chmod +x gradlew
- emulator64-x86 -avd test -no-audio -no-window & wait-for-emulator.sh
- wget -q https://f-droid.org/repo/org.dmfs.tasks_481.apk && adb install org.dmfs.tasks_481.apk
cache:
paths:
- .gradle/
test:
tags:
- privileged
script:
- ./gradlew check mergeAndroidReports
- start-emulator.sh
- ./gradlew app:check app:connectedCheck
artifacts:
paths:
- app/build/outputs/lint-results-debug.html
- app/build/reports
- build/reports
pages:
script:
- ./gradlew app:dokka
- mkdir public && mv app/build/dokka public
artifacts:
paths:
- public
only:
- master-ose
+3 -6
View File
@@ -1,12 +1,9 @@
[submodule "dav4android"]
path = dav4android
url = ../dav4android.git
[submodule "ical4android"]
path = ical4android
url = ../ical4android.git
url = https://gitlab.com/bitfireAT/ical4android.git
[submodule "vcard4android"]
path = vcard4android
url = ../vcard4android.git
url = https://gitlab.com/bitfireAT/vcard4android.git
[submodule "cert4android"]
path = cert4android
url = ../cert4android.git
url = https://gitlab.com/bitfireAT/cert4android.git
+47
View File
@@ -0,0 +1,47 @@
Contributing to DAVx⁵
=====================
**Thank you for your interest in contributing to DAVx⁵!**
Because you're reading this, you're probably interested in
contributing to the DAVx⁵ code. [Other ways to contribute:
see here.](https://www.davx5.com/donate#c306)
To contribute:
1. It's good idea to have a look at the [DAVx⁵ Roadmap](https://gitlab.com/bitfireAT/davx5-ose/wikis/Roadmap)
to see whether the change is already planned. Maybe there's even a link to a
corresponding forum thread there.
1. Determine which project the changes shall go to. There's
the DAVx⁵ main project (this repo), and the [related
libraries](README.md).
1. Please post to the [DAVx⁵ development forum](https://www.davx5.com/forums)
before doing actual work (unless you do it only for yourself, of course).
This will help to coordinate activities and you'll also get hints
about where to start and possible pitfalls.
1. Fork the repository.
1. Do the changes in your repository.
1. Submit a pull request to the original project.
1. Post in the forum again (to make sure the pull request is being notified).
Questions, discussion
=====================
We're happy to see questions, discussions etc. in the
[DAVx⁵ development forum](https://www.davx5.com/forums)!
Licensing
=========
All code has to be licensed under the GPL.
We (bitfire.at, initial developers) are also asking you to double-license the
code so that we can also use it for related non-open source projects like
[Managed DAVx⁵](https://www.davx5.com/organizations/managed-davx5).
Please find more about this in the Contributor's License Agreement (CLA)
we'll send to you if you want to contribute.
+19 -20
View File
@@ -1,38 +1,37 @@
![DAVx⁵ logo](app/src/main/res/mipmap-xxxhdpi/ic_launcher.png)
[![build status](https://gitlab.com/bitfireAT/davdroid/badges/master/build.svg)](https://gitlab.com/bitfireAT/davdroid/commits/master)
DAVdroid
DAVx⁵
========
Please see the [DAVdroid Web site](https://davdroid.bitfire.at) for
comprehensive information about DAVdroid.
Please see the [DAVx⁵ Web site](https://www.davx5.com) for
comprehensive information about DAVx⁵.
DAVdroid is licensed under the [GPLv3 License](LICENSE).
DAVx⁵ is licensed under the [GPLv3 License](LICENSE).
News and updates: [@davdroidapp](https://twitter.com/davdroidapp) on Twitter /
[davdroid-announce](https://davdroid.bitfire.at/download/newsletter/) mailing list
News and updates: [@davx5app](https://twitter.com/davx5app) on Twitter
Help and discussion: [DAVdroid forums](https://davdroid.bitfire.at/forums)
Help, discussion, feature requests, bug reports and "issues": [DAVx⁵ forums](https://www.davx5.com/forums)
**If you want to support DAVdroid, please consider [donating to DAVdroid](https://davdroid.bitfire.at/donate/)
or [purchasing it](https://davdroid.bitfire.at/download/).**
**If you want to support DAVx⁵, please consider [donating to DAVx⁵](https://www.davx5.com/donate)
or [purchasing it](https://www.davx5.com/download).**
Parts of DAVdroid have been outsourced into these libraries:
Generated KDoc: https://bitfireAT.gitlab.io/davx5-ose/dokka/app/
Parts of DAVx⁵ have been outsourced into these libraries:
* [cert4android](https://gitlab.com/bitfireAT/cert4android) custom certificate management
* [dav4android](https://gitlab.com/bitfireAT/dav4android) WebDAV/CalDav/CardDAV framework
* [dav4jvm](https://gitlab.com/bitfireAT/dav4jvm) WebDAV/CalDav/CardDAV framework
* [ical4android](https://gitlab.com/bitfireAT/ical4android) iCalendar processing and Calendar Provider access
* [vcard4android](https://gitlab.com/bitfireAT/vcard4android) VCard processing and Contacts Provider access
* [vcard4android](https://gitlab.com/bitfireAT/vcard4android) vCard processing and Contacts Provider access
USED THIRD-PARTY LIBRARIES
==========================
Those libraries are used by DAVdroid (alphabetically):
Those libraries are used by DAVx⁵ (alphabetically):
* [Color Picker](https://github.com/jaredrummler/ColorPicker) [Apache License, Version 2.0](https://github.com/jaredrummler/ColorPicker/LICENSE)
* [dnsjava](http://www.xbill.org/dnsjava/) [BSD License](http://www.xbill.org/dnsjava/dnsjava-current/LICENSE)
* [ez-vcard](https://code.google.com/p/ez-vcard/) [New BSD License](http://opensource.org/licenses/BSD-3-Clause)
* [iCal4j](http://ical4j.sourceforge.net/) [New BSD License](http://sourceforge.net/p/ical4j/ical4j/ci/default/tree/LICENSE)
* [okhttp](https://square.github.io/okhttp/) [Apache License, Version 2.0](https://square.github.io/okhttp/#license)
* [Project Lombok](http://projectlombok.org/) [MIT License](http://opensource.org/licenses/mit-license.php)
* [ez-vcard](https://github.com/mangstadt/ez-vcard) [New BSD License](http://opensource.org/licenses/BSD-3-Clause)
* [iCal4j](https://github.com/ical4j/ical4j) [New BSD License](http://sourceforge.net/p/ical4j/ical4j/ci/default/tree/LICENSE)
* [okhttp](https://square.github.io/okhttp) [Apache License, Version 2.0](https://square.github.io/okhttp/#license)
+115 -102
View File
@@ -7,151 +7,164 @@
*/
apply plugin: 'com.android.application'
apply plugin: 'com.mikepenz.aboutlibraries.plugin'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'org.jetbrains.dokka'
android {
compileSdkVersion 26
buildToolsVersion '26.0.0'
compileSdkVersion 30
buildToolsVersion '30.0.3'
defaultConfig {
applicationId "at.bitfire.davdroid"
resValue "string", "packageID", applicationId
versionCode 160
versionCode 303100002
buildConfigField "long", "buildTime", System.currentTimeMillis() + "L"
minSdkVersion 16
targetSdkVersion 25
minSdkVersion 21 // Android 5
targetSdkVersion 30 // Android 11
buildConfigField "boolean", "customCerts", "false"
buildConfigField "at.bitfire.vcard4android.GroupMethod", "settingContactGroupMethod", "null"
buildConfigField "String", "userAgent", "\"DAVx5\""
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
kapt {
arguments {
arg("room.schemaLocation", "$projectDir/schemas")
}
}
}
compileOptions {
// enable because ical4android requires desugaring
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures.dataBinding = true
flavorDimensions "distribution"
productFlavors {
standard {
versionName "1.6.5"
buildConfigField "boolean", "customCerts", "true"
}
gplay {
versionName "1.6.5-play"
buildConfigField "boolean", "customCerts", "true"
}
icloud {
applicationId "at.bitfire.cloudsync"
resValue "string", "packageID", applicationId
versionName "1.6.5-cloud"
buildConfigField "at.bitfire.vcard4android.GroupMethod", "settingContactGroupMethod", "at.bitfire.vcard4android.GroupMethod.GROUP_VCARDS"
}
soldupe {
applicationId "com.soldupe.cloudsync"
resValue "string", "packageID", applicationId
minSdkVersion 21
versionName "1.6.5-soldupe"
buildConfigField "boolean", "customCerts", "true"
buildConfigField "at.bitfire.vcard4android.GroupMethod", "settingContactGroupMethod", "at.bitfire.vcard4android.GroupMethod.CATEGORIES"
versionName "3.3.10-rc1-ose"
}
}
sourceSets {
standard.java.srcDirs = [ "src/davdroid/java" ]
standard.res.srcDirs = [ "src/davdroid/res" ]
gplay.java.srcDirs = [ "src/gplay/java", "src/davdroid/java" ]
gplay.res.srcDirs = [ "src/gplay/res", "src/davdroid/res" ]
}
signingConfigs {
bitfire {
storeFile file("${System.env.HOME}/Entwicklung/GooglePlay/bitfire.jks")
storePassword '***REMOVED***'
keyAlias 'bitfire'
keyPassword '***REMOVED***'
}
soldupe {
storeFile file("${System.env.HOME}/Entwicklung/GooglePlay/soldupe.jks")
storePassword 'hei8eePh'
keyAlias 'soldupe'
keyPassword 'ocaip6oZ'
}
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
}
buildTypes {
debug {
minifyEnabled false
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules-release.pro'
signingConfig signingConfigs.bitfire
productFlavors.soldupe.signingConfig signingConfigs.soldupe
shrinkResources true
}
}
lintOptions {
disable 'GoogleAppIndexingWarning' // we don't need Google indexing, thanks
disable 'GradleDependency'
disable 'IconColors'
disable 'IconLauncherShape'
disable 'IconMissingDensityFolder'
disable 'ImpliedQuantity', 'MissingQuantity'
disable 'MissingTranslation', 'ExtraTranslation' // translations from Transifex are not always up to date
disable 'Recycle' // doesn't understand Lombok's @Cleanup
disable 'RtlEnabled'
disable 'RtlHardcoded'
disable '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 'RtlEnabled', 'RtlHardcoded' // RTL not supported yet
disable 'Typos'
}
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/LICENSE'
disable 'NullSafeMutableLiveData' // crashes lint since Mar 2021
}
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
dokka.configuration {
sourceLink {
url = "https://gitlab.com/bitfireAT/davx5-ose/tree/master-ose/"
lineSuffix = "#L"
}
jdkVersion = 8
externalDocumentationLink {
url = new URL("https://bitfireat.gitlab.io/cert4android/dokka/cert4android/")
packageListUrl = new URL("https://bitfireat.gitlab.io/cert4android/dokka/cert4android/package-list")
}
externalDocumentationLink {
url = new URL("https://bitfireat.gitlab.io/dav4jvm/dokka/dav4jvm/")
packageListUrl = new URL("https://bitfireat.gitlab.io/dav4jvm/dokka/dav4jvm/package-list")
}
externalDocumentationLink {
url = new URL("https://bitfireat.gitlab.io/ical4android/dokka/ical4android/")
packageListUrl = new URL("https://bitfireat.gitlab.io/ical4android/dokka/ical4android/package-list")
}
externalDocumentationLink {
url = new URL("https://bitfireat.gitlab.io/vcard4android/dokka/vcard4android/")
packageListUrl = new URL("https://bitfireat.gitlab.io/vcard4android/dokka/vcard4android/package-list")
}
}
}
aboutLibraries {
additionalLicenses {
mpl_2_0
}
}
dependencies {
compile project(':cert4android')
compile project(':dav4android')
compile project(':ical4android')
compile project(':vcard4android')
implementation project(':cert4android')
implementation project(':ical4android')
implementation project(':vcard4android')
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
//noinspection GradleDynamicVersion
compile 'com.android.support:appcompat-v7:26.+'
//noinspection GradleDynamicVersion
compile 'com.android.support:cardview-v7:26.+'
//noinspection GradleDynamicVersion
compile 'com.android.support:design:26.+'
//noinspection GradleDynamicVersion
compile 'com.android.support:preference-v14:26.+'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.browser:browser:1.3.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.fragment:fragment-ktx:1.3.2'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
implementation 'androidx.paging:paging-runtime-ktx:2.1.2'
implementation 'androidx.preference:preference-ktx:1.1.1'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'com.google.android:flexbox:1.1.0'
implementation 'com.google.android.material:material:1.3.0'
compile 'com.github.yukuku:ambilwarna:2.0.1'
def room_version = '2.2.6'
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$room_version"
androidTestImplementation "androidx.room:room-testing:$room_version"
compile 'com.squareup.okhttp3:logging-interceptor:3.8.1'
compile 'commons-io:commons-io:2.5'
compile 'dnsjava:dnsjava:2.1.8'
compile 'org.apache.commons:commons-lang3:3.6'
compile 'org.apache.commons:commons-collections4:4.1'
provided 'org.projectlombok:lombok:1.16.16'
implementation 'com.jaredrummler:colorpicker:1.1.0'
implementation "com.github.AppIntro:AppIntro:${versions.appIntro}"
implementation "com.gitlab.bitfireAT:dav4jvm:${versions.dav4jvm}"
implementation "com.mikepenz:aboutlibraries:${versions.aboutLibraries}"
implementation "com.squareup.okhttp3:okhttp:${versions.okhttp}"
implementation "com.squareup.okhttp3:okhttp-brotli:${versions.okhttp}"
implementation "com.squareup.okhttp3:logging-interceptor:${versions.okhttp}"
implementation 'commons-io:commons-io:2.6'
//noinspection GradleDependency - dnsjava 3+ needs Java 8/Android 7
implementation 'dnsjava:dnsjava:2.1.9'
//noinspection GradleDependency
implementation "org.apache.commons:commons-collections4:${versions.commonsCollections}"
//noinspection GradleDependency
implementation "org.apache.commons:commons-lang3:${versions.commonsLang}"
//noinspection GradleDependency
implementation "org.apache.commons:commons-text:${versions.commonsText}"
// for tests
//noinspection GradleDynamicVersion
androidTestCompile('com.android.support.test:runner:+') {
exclude group: 'com.android.support', module: 'support-annotations'
}
//noinspection GradleDynamicVersion
androidTestCompile('com.android.support.test:rules:+') {
exclude group: 'com.android.support', module: 'support-annotations'
}
androidTestCompile 'junit:junit:4.12'
androidTestCompile 'com.squareup.okhttp3:mockwebserver:3.8.1'
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test:rules:1.3.0'
androidTestImplementation 'junit:junit:4.13.2'
androidTestImplementation "com.squareup.okhttp3:mockwebserver:${versions.okhttp}"
testCompile 'junit:junit:4.12'
testCompile 'com.squareup.okhttp3:mockwebserver:3.8.1'
testImplementation 'junit:junit:4.13.2'
testImplementation "com.squareup.okhttp3:mockwebserver:${versions.okhttp}"
}
+25
View File
@@ -0,0 +1,25 @@
# R8 usage for DAVx⁵:
# shrinking yes (only in release builds)
# optimization yes (on by R8 defaults)
# 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.** { *; }
# DAVx + libs
-keep class at.bitfire.** { *; } # all DAVx code is required
# 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);
}
-39
View File
@@ -1,39 +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
# 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
-dontwarn sun.misc.Perf
-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 org.codehaus.groovy.**
-dontwarn net.fortuna.ical4j.model.** # ignore warnings from Groovy dependency
-dontwarn org.apache.log4j.** # ignore warnings from log4j dependency
-keep class net.fortuna.ical4j.** { *; } # keep all model classes (properties/factories, created at runtime)
# okhttp
-dontwarn okio.**
-dontwarn javax.annotation.Nullable
-dontwarn javax.annotation.ParametersAreNonnullByDefault
# dnsjava
-dontwarn sun.net.spi.nameservice.** # not available on Android
# DAVdroid + libs
-keep class at.bitfire.** { *; } # all DAVdroid code is required
@@ -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')"
]
}
}
+1
View File
@@ -0,0 +1 @@
espressoTest
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="at.bitfire.davdroid"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
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>
<service
android:name=".syncadapter.NullAuthenticatorService"
android:exported="false">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/test_account_authenticator"/>
</service>
</application>
</manifest>
@@ -0,0 +1,74 @@
/*
* 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 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.Before
import org.junit.Test
class HttpClientTest {
lateinit var server: MockWebServer
lateinit var httpClient: HttpClient
@Before
fun setUp() {
httpClient = HttpClient.Builder().build()
server = MockWebServer()
server.start(30000)
}
@After
fun tearDown() {
server.shutdown()
httpClient.close()
}
@Test
fun testCookies() {
val url = server.url("/test")
// set cookie for root path (/) and /test path in first response
server.enqueue(MockResponse()
.setResponseCode(200)
.addHeader("Set-Cookie", "cookie1=1; path=/")
.addHeader("Set-Cookie", "cookie2=2")
.setBody("Cookie set"))
httpClient.okHttpClient.newCall(Request.Builder()
.get().url(url)
.build()).execute()
assertNull(server.takeRequest().getHeader("Cookie"))
// cookie should be sent with second request
// second response lets first cookie expire and overwrites second cookie
server.enqueue(MockResponse()
.addHeader("Set-Cookie", "cookie1=1a; path=/; Max-Age=0")
.addHeader("Set-Cookie", "cookie2=2a")
.setResponseCode(200))
httpClient.okHttpClient.newCall(Request.Builder()
.get().url(url)
.build()).execute()
assertEquals("cookie2=2; cookie1=1", server.takeRequest().getHeader("Cookie"))
server.enqueue(MockResponse()
.setResponseCode(200))
httpClient.okHttpClient.newCall(Request.Builder()
.get().url(url)
.build()).execute()
assertEquals("cookie2=2a", server.takeRequest().getHeader("Cookie"))
}
}
@@ -1,57 +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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.net.Socket;
import javax.net.ssl.SSLSocket;
import at.bitfire.cert4android.CustomCertManager;
import okhttp3.mockwebserver.MockWebServer;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.TestCase.assertFalse;
import static org.apache.commons.lang3.ArrayUtils.contains;
import static org.junit.Assert.assertTrue;
public class SSLSocketFactoryCompatTest {
SSLSocketFactoryCompat factory;
MockWebServer server = new MockWebServer();
@Before
public void startServer() throws Exception {
factory = new SSLSocketFactoryCompat(new CustomCertManager(getTargetContext().getApplicationContext(), true));
server.start();
}
@After
public void stopServer() throws Exception {
server.shutdown();
}
@Test
public void testUpgradeTLS() throws IOException {
Socket s = factory.createSocket(server.getHostName(), server.getPort());
assertTrue(s instanceof SSLSocket);
SSLSocket ssl = (SSLSocket)s;
assertFalse(contains(ssl.getEnabledProtocols(), "SSLv3"));
assertTrue(contains(ssl.getEnabledProtocols(), "TLSv1"));
assertTrue(contains(ssl.getEnabledProtocols(), "TLSv1.1"));
assertTrue(contains(ssl.getEnabledProtocols(), "TLSv1.2"));
}
}
@@ -0,0 +1,37 @@
package at.bitfire.davdroid.model
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"
@Rule
@JvmField
val helper = MigrationTestHelper(
InstrumentationRegistry.getInstrumentation(),
AppDatabase::class.java.canonicalName,
FrameworkSQLiteOpenHelperFactory()
)
@Test
fun testAllMigrations() {
helper.createDatabase(TEST_DB, 8).close()
Room.databaseBuilder(
InstrumentationRegistry.getInstrumentation().targetContext,
AppDatabase::class.java,
TEST_DB
).addMigrations(*AppDatabase.migrations).build().apply {
openHelper.writableDatabase
close()
}
}
}
@@ -1,117 +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 org.junit.Test;
import java.io.IOException;
import at.bitfire.dav4android.DavResource;
import at.bitfire.dav4android.exception.DavException;
import at.bitfire.dav4android.exception.HttpException;
import at.bitfire.dav4android.property.ResourceType;
import at.bitfire.davdroid.HttpClient;
import at.bitfire.davdroid.model.ServiceDB.Collections;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class CollectionInfoTest {
MockWebServer server = new MockWebServer();
@Test
public void testFromDavResource() throws IOException, HttpException, DavException {
// r/w address book
server.enqueue(new MockResponse()
.setResponseCode(207)
.setBody("<multistatus xmlns='DAV:' xmlns:CARD='urn:ietf:params:xml:ns:carddav'>" +
"<response>" +
" <href>/</href>" +
" <propstat><prop>" +
" <resourcetype><collection/><CARD:addressbook/></resourcetype>" +
" <displayname>My Contacts</displayname>" +
" <CARD:addressbook-description>My Contacts Description</CARD:addressbook-description>" +
" </prop></propstat>" +
"</response>" +
"</multistatus>"));
DavResource dav = new DavResource(HttpClient.create(null), server.url("/"));
dav.propfind(0, ResourceType.NAME);
CollectionInfo info = new CollectionInfo(dav);
assertEquals(CollectionInfo.Type.ADDRESS_BOOK, info.getType());
assertFalse(info.getReadOnly());
assertEquals("My Contacts", info.getDisplayName());
assertEquals("My Contacts Description", info.getDescription());
// read-only calendar, no display name
server.enqueue(new MockResponse()
.setResponseCode(207)
.setBody("<multistatus xmlns='DAV:' xmlns:CAL='urn:ietf:params:xml:ns:caldav' xmlns:ICAL='http://apple.com/ns/ical/'>" +
"<response>" +
" <href>/</href>" +
" <propstat><prop>" +
" <resourcetype><collection/><CAL:calendar/></resourcetype>" +
" <current-user-privilege-set><privilege><read/></privilege></current-user-privilege-set>" +
" <CAL:calendar-description>My Calendar</CAL:calendar-description>" +
" <CAL:calendar-timezone>tzdata</CAL:calendar-timezone>" +
" <ICAL:calendar-color>#ff0000</ICAL:calendar-color>" +
" </prop></propstat>" +
"</response>" +
"</multistatus>"));
dav = new DavResource(HttpClient.create(null), server.url("/"));
dav.propfind(0, ResourceType.NAME);
info = new CollectionInfo(dav);
assertEquals(CollectionInfo.Type.CALENDAR, info.getType());
assertTrue(info.getReadOnly());
assertNull(info.getDisplayName());
assertEquals("My Calendar", info.getDescription());
assertEquals(0xFFFF0000, (int)info.getColor());
assertEquals("tzdata", info.getTimeZone());
assertTrue(info.getSupportsVEVENT());
assertTrue(info.getSupportsVTODO());
}
@Test
public void testFromDB() {
ContentValues values = new ContentValues();
values.put(Collections.ID, 1);
values.put(Collections.SERVICE_ID, 1);
values.put(Collections.URL, "http://example.com");
values.put(Collections.READ_ONLY, 1);
values.put(Collections.DISPLAY_NAME, "display name");
values.put(Collections.DESCRIPTION, "description");
values.put(Collections.COLOR, 0xFFFF0000);
values.put(Collections.TIME_ZONE, "tzdata");
values.put(Collections.SUPPORTS_VEVENT, 1);
values.put(Collections.SUPPORTS_VTODO, 1);
values.put(Collections.SYNC, 1);
CollectionInfo info = new CollectionInfo(values);
assertEquals(1, (long)info.getId());
assertEquals(1, (long)info.getServiceID());
assertEquals("http://example.com", info.getUrl());
assertTrue(info.getReadOnly());
assertEquals("display name", info.getDisplayName());
assertEquals("description", info.getDescription());
assertEquals(0xFFFF0000, (int)info.getColor());
assertEquals("tzdata", info.getTimeZone());
assertTrue(info.getSupportsVEVENT());
assertTrue(info.getSupportsVTODO());
assertTrue(info.getSelected());
}
}
@@ -0,0 +1,134 @@
/*
* 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 androidx.test.filters.SmallTest
import at.bitfire.dav4jvm.DavResource
import at.bitfire.dav4jvm.property.ResourceType
import at.bitfire.davdroid.HttpClient
import okhttp3.HttpUrl.Companion.toHttpUrl
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 CollectionTest {
private lateinit var httpClient: HttpClient
private val server = MockWebServer()
@Before
fun setUp() {
httpClient = HttpClient.Builder().build()
}
@After
fun shutDown() {
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)
}
}
@@ -0,0 +1,64 @@
package at.bitfire.davdroid.model
import androidx.room.Room
import androidx.test.platform.app.InstrumentationRegistry
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import org.junit.After
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
class DaoToolsTest {
private lateinit var db: AppDatabase
@Before
fun createDb() {
val context = InstrumentationRegistry.getInstrumentation().context
db = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java).build()
}
@After
fun closeDb() {
db.close()
}
@Test
fun testSyncAll() {
val serviceDao = db.serviceDao()
val service = Service(id=0, accountName="test", type=Service.TYPE_CALDAV, principal = null)
service.id = serviceDao.insertOrReplace(service)
val homeSetDao = db.homeSetDao()
val entry1 = HomeSet(id=1, serviceId=service.id, personal=true, url= "https://example.com/1".toHttpUrl())
val entry3 = HomeSet(id=3, serviceId=service.id, personal=true, url= "https://example.com/3".toHttpUrl())
val oldItems = listOf(
entry1,
HomeSet(id=2, serviceId=service.id, personal=true, url= "https://example.com/2".toHttpUrl()),
entry3
)
homeSetDao.insert(oldItems)
val newItems = mutableMapOf<HttpUrl, HomeSet>()
newItems[entry1.url] = entry1
// no id, because identity is given by the url
val updated = HomeSet(id=0, serviceId=service.id, personal=true,
url= "https://example.com/2".toHttpUrl(), displayName="Updated Entry")
newItems[updated.url] = updated
val created = HomeSet(id=4, serviceId=service.id, personal=true, url= "https://example.com/4".toHttpUrl())
newItems[created.url] = created
DaoTools(homeSetDao).syncAll(oldItems, newItems, { it.url })
val afterSync = homeSetDao.getByService(service.id)
assertEquals(afterSync.size, 3)
assertFalse(afterSync.contains(entry3))
assertTrue(afterSync.contains(entry1))
assertTrue(afterSync.contains(updated))
assertTrue(afterSync.contains(created))
}
}
@@ -0,0 +1,72 @@
package at.bitfire.davdroid.settings
import android.accounts.Account
import android.accounts.AccountManager
import android.content.ContentResolver
import android.os.Build
import android.provider.CalendarContract
import android.provider.ContactsContract
import androidx.test.platform.app.InstrumentationRegistry
import at.bitfire.davdroid.R
import at.bitfire.davdroid.model.Credentials
import at.bitfire.davdroid.syncadapter.AccountUtils
import org.junit.After
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import java.util.concurrent.TimeUnit
class AccountSettingsTest {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val account = Account("Test Account", context.getString(R.string.account_type))
val fakeCredentials = Credentials("test", "test")
@Before
fun prepareAccount() {
assertTrue(AccountUtils.createAccount(context, account, AccountSettings.initialUserData(fakeCredentials)))
ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 1)
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 0)
}
@After
fun removeAccount() {
val futureResult = AccountManager.get(context).removeAccount(account, {}, null)
assertTrue(futureResult.getResult(10, TimeUnit.SECONDS))
}
@Test
fun testSyncIntervals() {
val settings = AccountSettings(context, account)
val presetIntervals = context.resources.getStringArray(R.array.settings_sync_interval_seconds)
.map { it.toLong() }
.filter { it != AccountSettings.SYNC_INTERVAL_MANUALLY }
for (interval in presetIntervals) {
assertTrue(settings.setSyncInterval(CalendarContract.AUTHORITY, interval))
assertEquals(interval, settings.getSyncInterval(CalendarContract.AUTHORITY))
}
}
@Test
fun testSyncIntervals_IsNotSyncable() {
val settings = AccountSettings(context, account)
val interval = 15*60L // 15 min
val result = settings.setSyncInterval(ContactsContract.AUTHORITY, interval)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) // below Android 7, Android returns true for whatever reason
assertFalse(result)
}
@Test
fun testSyncIntervals_TooShort() {
val settings = AccountSettings(context, account)
val interval = 60L // 1 min is not supported by Android
val result = settings.setSyncInterval(CalendarContract.AUTHORITY, interval)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) // below Android 7, Android returns true for whatever reason
assertFalse(result)
}
}
@@ -0,0 +1,28 @@
/*
* 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 androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
class SettingsManagerTest {
val settingsManager by lazy { SettingsManager.getInstance(InstrumentationRegistry.getInstrumentation().targetContext) }
@Test
fun testContainsKey() {
assertFalse(settingsManager.containsKey("notExisting"))
// provided by DefaultsProvider
assertTrue(settingsManager.containsKey(Settings.OVERRIDE_PROXY))
}
}
@@ -0,0 +1,36 @@
package at.bitfire.davdroid.syncadapter
import android.accounts.Account
import android.accounts.AccountManager
import android.os.Bundle
import androidx.test.platform.app.InstrumentationRegistry
import at.bitfire.davdroid.R
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import java.util.concurrent.TimeUnit
class AccountUtilsTest {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val account = Account("Test Account", context.getString(R.string.account_type))
@Test
fun testCreateAccount() {
val userData = Bundle(2)
userData.putString("int", "1")
userData.putString("string", "abc/\"-")
try {
assertTrue(AccountUtils.createAccount(context, account, userData))
// validate user data
val manager = AccountManager.get(context)
assertEquals("1", manager.getUserData(account, "int"))
assertEquals("abc/\"-", manager.getUserData(account, "string"))
} finally {
val futureResult = AccountManager.get(context).removeAccount(account, {}, null)
assertTrue(futureResult.getResult(10, TimeUnit.SECONDS))
}
}
}
@@ -0,0 +1,38 @@
package at.bitfire.davdroid.syncadapter
import at.bitfire.davdroid.model.SyncState
import at.bitfire.davdroid.resource.LocalCollection
class LocalTestCollection: LocalCollection<LocalTestResource> {
override val tag = "LocalTestCollection"
override val title = "Local Test Collection"
override var lastSyncState: SyncState? = null
val entries = mutableListOf<LocalTestResource>()
override fun findDeleted() = entries.filter { it.deleted }
override fun findDirty() = entries.filter { it.dirty }
override fun findByName(name: String) = entries.filter { it.fileName == name }.firstOrNull()
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() {
}
}
@@ -0,0 +1,43 @@
package at.bitfire.davdroid.syncadapter
import android.net.Uri
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(): Uri {
TODO("Not yet implemented")
}
override fun update(data: Any): Uri {
TODO("Not yet implemented")
}
override fun delete(): Int {
TODO("Not yet implemented")
}
}
@@ -0,0 +1,31 @@
package at.bitfire.davdroid.syncadapter
import android.os.Bundle
import androidx.test.filters.SmallTest
import at.bitfire.davdroid.syncadapter.SyncAdapterService.SyncAdapter
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertTrue
import org.junit.Test
class SyncAdapterServiceTest {
@Test
@SmallTest
fun testPriorityCollections() {
val extras = Bundle()
assertTrue(SyncAdapter.priorityCollections(extras).isEmpty())
extras.putString(SyncAdapterService.SYNC_EXTRAS_PRIORITY_COLLECTIONS, "")
assertTrue(SyncAdapter.priorityCollections(extras).isEmpty())
extras.putString(SyncAdapterService.SYNC_EXTRAS_PRIORITY_COLLECTIONS, "123")
assertArrayEquals(longArrayOf(123), SyncAdapter.priorityCollections(extras).toLongArray())
extras.putString(SyncAdapterService.SYNC_EXTRAS_PRIORITY_COLLECTIONS, ",x,")
assertTrue(SyncAdapter.priorityCollections(extras).isEmpty())
extras.putString(SyncAdapterService.SYNC_EXTRAS_PRIORITY_COLLECTIONS, "1,2,3")
assertArrayEquals(longArrayOf(1,2,3), SyncAdapter.priorityCollections(extras).toLongArray())
}
}
@@ -0,0 +1,437 @@
package at.bitfire.davdroid.syncadapter
import android.accounts.Account
import android.accounts.AccountManager
import android.content.SyncResult
import android.os.Bundle
import androidx.core.app.NotificationManagerCompat
import androidx.test.platform.app.InstrumentationRegistry
import at.bitfire.dav4jvm.PropStat
import at.bitfire.dav4jvm.Response
import at.bitfire.dav4jvm.Response.HrefRelation
import at.bitfire.dav4jvm.property.GetETag
import at.bitfire.davdroid.R
import at.bitfire.davdroid.model.Credentials
import at.bitfire.davdroid.model.SyncState
import at.bitfire.davdroid.settings.AccountSettings
import okhttp3.Protocol
import okhttp3.internal.http.StatusLine
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.junit.*
import org.junit.Assert.*
import java.util.concurrent.TimeUnit
class SyncManagerTest {
companion object {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val account = Account("SyncManagerTest", context.getString(R.string.account_type))
@BeforeClass
@JvmStatic
fun createAccount() {
assertTrue(AccountManager.get(context).addAccountExplicitly(account, "test", AccountSettings.initialUserData(Credentials("test", "test"))))
}
@AfterClass
@JvmStatic
fun removeAccount() {
assertTrue(AccountManager.get(context).removeAccount(account, null, null).getResult(10, TimeUnit.SECONDS))
// clear annoying syncError notifications
NotificationManagerCompat.from(context).cancelAll()
}
}
val server = MockWebServer()
private fun syncManager(collection: LocalTestCollection) =
TestSyncManager(
context,
account,
Bundle(),
"TestAuthority",
SyncResult(),
collection,
server
)
@Before
fun startServer() {
server.start()
}
@After
fun stopServer() {
server.close()
}
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>")
val response = MockResponse()
.setResponseCode(207)
.setHeader("Content-Type", "text/xml")
.setBody(body.toString())
return response
}
@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())
}
}
@@ -0,0 +1,100 @@
package at.bitfire.davdroid.syncadapter
import android.accounts.Account
import android.content.Context
import android.content.SyncResult
import android.os.Bundle
import at.bitfire.dav4jvm.DavCollection
import at.bitfire.dav4jvm.DavResponseCallback
import at.bitfire.dav4jvm.Response
import at.bitfire.dav4jvm.property.GetCTag
import at.bitfire.davdroid.DavUtils
import at.bitfire.davdroid.model.SyncState
import at.bitfire.davdroid.resource.LocalResource
import at.bitfire.davdroid.settings.AccountSettings
import okhttp3.HttpUrl
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.mockwebserver.MockWebServer
import org.junit.Assert.assertEquals
class TestSyncManager(
context: Context,
account: Account,
extras: Bundle,
authority: String,
syncResult: SyncResult,
localCollection: LocalTestCollection,
val mockWebServer: MockWebServer
): SyncManager<LocalTestResource, LocalTestCollection, DavCollection>(context, account, AccountSettings(context, account), extras, authority, syncResult, localCollection) {
override fun prepare(): Boolean {
collectionURL = mockWebServer.url("/")
davCollection = DavCollection(httpClient.okHttpClient, collectionURL)
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: DavResponseCallback) {
if (didListAllRemote)
throw IllegalStateException("listAllRemote() must not be called twice")
didListAllRemote = true
for (result in listAllRemoteResult)
callback(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 = DavUtils.lastSegmentOfUrl(url)
var localEntry = localCollection.entries.filter { it.fileName == fileName }.firstOrNull()
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(): String {
TODO("Not yet implemented")
}
}
@@ -1,200 +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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.net.URI;
import at.bitfire.dav4android.DavResource;
import at.bitfire.dav4android.exception.DavException;
import at.bitfire.dav4android.exception.HttpException;
import at.bitfire.dav4android.property.AddressbookHomeSet;
import at.bitfire.dav4android.property.ResourceType;
import at.bitfire.davdroid.HttpClient;
import at.bitfire.davdroid.log.Logger;
import at.bitfire.davdroid.ui.setup.DavResourceFinder.Configuration.ServiceInfo;
import okhttp3.OkHttpClient;
import okhttp3.mockwebserver.Dispatcher;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class DavResourceFinderTest {
MockWebServer server = new MockWebServer();
DavResourceFinder finder;
OkHttpClient client;
LoginCredentials credentials;
private static final String
PATH_NO_DAV = "/nodav",
PATH_CALDAV = "/caldav",
PATH_CARDDAV = "/carddav",
PATH_CALDAV_AND_CARDDAV = "/both-caldav-carddav",
SUBPATH_PRINCIPAL = "/principal",
SUBPATH_ADDRESSBOOK_HOMESET = "/addressbooks",
SUBPATH_ADDRESSBOOK = "/addressbooks/private-contacts";
@Before
public void initServerAndClient() throws Exception {
server.setDispatcher(new TestDispatcher());
server.start();
credentials = new LoginCredentials(URI.create("/"), "mock", "12345");
finder = new DavResourceFinder(getTargetContext(), credentials);
client = HttpClient.create(null);
client = HttpClient.addAuthentication(client, credentials.getUserName(), credentials.getPassword());
}
@After
public void stopServer() throws Exception {
server.shutdown();
}
@Test
public void testRememberIfAddressBookOrHomeset() throws IOException, HttpException, DavException {
ServiceInfo info;
// before dav.propfind(), no info is available
DavResource dav = new DavResource(client, server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL));
finder.rememberIfAddressBookOrHomeset(dav, info = new ServiceInfo());
assertEquals(0, info.getCollections().size());
assertEquals(0, info.getHomeSets().size());
// recognize home set
dav.propfind(0, AddressbookHomeSet.NAME);
finder.rememberIfAddressBookOrHomeset(dav, info = new ServiceInfo());
assertEquals(0, info.getCollections().size());
assertEquals(1, info.getHomeSets().size());
assertEquals(server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK_HOMESET + "/").uri(), info.getHomeSets().iterator().next());
// recognize address book
dav = new DavResource(client, server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK));
dav.propfind(0, ResourceType.NAME);
finder.rememberIfAddressBookOrHomeset(dav, info = new ServiceInfo());
assertEquals(1, info.getCollections().size());
assertEquals(server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK + "/").uri(), info.getCollections().keySet().iterator().next());
assertEquals(0, info.getHomeSets().size());
}
@Test
public void testProvidesService() throws IOException {
assertFalse(finder.providesService(server.url(PATH_NO_DAV), DavResourceFinder.Service.CALDAV));
assertFalse(finder.providesService(server.url(PATH_NO_DAV), DavResourceFinder.Service.CARDDAV));
assertTrue(finder.providesService(server.url(PATH_CALDAV), DavResourceFinder.Service.CALDAV));
assertFalse(finder.providesService(server.url(PATH_CALDAV), DavResourceFinder.Service.CARDDAV));
assertTrue(finder.providesService(server.url(PATH_CARDDAV), DavResourceFinder.Service.CARDDAV));
assertFalse(finder.providesService(server.url(PATH_CARDDAV), DavResourceFinder.Service.CALDAV));
assertTrue(finder.providesService(server.url(PATH_CALDAV_AND_CARDDAV), DavResourceFinder.Service.CALDAV));
assertTrue(finder.providesService(server.url(PATH_CALDAV_AND_CARDDAV), DavResourceFinder.Service.CARDDAV));
}
@Test
public void testGetCurrentUserPrincipal() throws IOException, HttpException, DavException {
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_NO_DAV), DavResourceFinder.Service.CALDAV));
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_NO_DAV), DavResourceFinder.Service.CARDDAV));
assertEquals(
server.url(PATH_CALDAV + SUBPATH_PRINCIPAL).uri(),
finder.getCurrentUserPrincipal(server.url(PATH_CALDAV), DavResourceFinder.Service.CALDAV)
);
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_CALDAV), DavResourceFinder.Service.CARDDAV));
assertEquals(
server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL).uri(),
finder.getCurrentUserPrincipal(server.url(PATH_CARDDAV), DavResourceFinder.Service.CARDDAV)
);
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_CARDDAV), DavResourceFinder.Service.CALDAV));
}
// mock server
public class TestDispatcher extends Dispatcher {
@Override
public MockResponse dispatch(RecordedRequest rq) throws InterruptedException {
if (!checkAuth(rq)) {
MockResponse authenticate = new MockResponse().setResponseCode(401);
authenticate.setHeader("WWW-Authenticate", "Basic realm=\"test\"");
return authenticate;
}
String path = rq.getPath();
if ("OPTIONS".equalsIgnoreCase(rq.getMethod())) {
String dav = null;
if (path.startsWith(PATH_CALDAV))
dav = "calendar-access";
else if (path.startsWith(PATH_CARDDAV))
dav = "addressbook";
else if (path.startsWith(PATH_CALDAV_AND_CARDDAV))
dav = "calendar-access, addressbook";
MockResponse response = new MockResponse().setResponseCode(200);
if (dav != null)
response.addHeader("DAV", dav);
return response;
} else if ("PROPFIND".equalsIgnoreCase(rq.getMethod())) {
String props = null;
switch (path) {
case PATH_CALDAV:
case PATH_CARDDAV:
props = "<current-user-principal><href>" + path + SUBPATH_PRINCIPAL + "</href></current-user-principal>";
break;
case PATH_CARDDAV + SUBPATH_PRINCIPAL:
props = "<CARD:addressbook-home-set>" +
" <href>" + PATH_CARDDAV + SUBPATH_ADDRESSBOOK_HOMESET + "</href>" +
"</CARD:addressbook-home-set>";
break;
case PATH_CARDDAV + SUBPATH_ADDRESSBOOK:
props = "<resourcetype>" +
" <collection/>" +
" <CARD:addressbook/>" +
"</resourcetype>";
break;
}
Logger.log.info("Sending props: " + props);
return new MockResponse()
.setResponseCode(207)
.setBody("<multistatus xmlns='DAV:' xmlns:CARD='urn:ietf:params:xml:ns:carddav'>" +
"<response>" +
" <href>" + rq.getPath() + "</href>" +
" <propstat><prop>" + props + "</prop></propstat>" +
"</response>" +
"</multistatus>");
}
return new MockResponse().setResponseCode(404);
}
private boolean checkAuth(RecordedRequest rq) {
return "Basic bW9jazoxMjM0NQ==".equals(rq.getHeader("Authorization"));
}
}
}
@@ -0,0 +1,208 @@
/*
* 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 androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import at.bitfire.dav4jvm.DavResource
import at.bitfire.dav4jvm.property.AddressbookHomeSet
import at.bitfire.dav4jvm.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 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.Before
import org.junit.Test
import java.net.URI
class DavResourceFinderTest {
companion object {
private const val PATH_NO_DAV = "/nodav"
private const val PATH_CALDAV = "/caldav"
private const val PATH_CARDDAV = "/carddav"
private const val PATH_CALDAV_AND_CARDDAV = "/both-caldav-carddav"
private const val SUBPATH_PRINCIPAL = "/principal"
private const val SUBPATH_ADDRESSBOOK_HOMESET = "/addressbooks"
private const val SUBPATH_ADDRESSBOOK = "/addressbooks/private-contacts"
}
val server = MockWebServer()
lateinit var finder: DavResourceFinder
lateinit var client: HttpClient
lateinit var loginModel: LoginModel
@Before
fun initServerAndClient() {
server.dispatcher = TestDispatcher()
server.start()
loginModel = LoginModel()
loginModel.baseURI = URI.create("/")
loginModel.credentials = Credentials("mock", "12345")
finder = DavResourceFinder(InstrumentationRegistry.getInstrumentation().targetContext, loginModel)
client = HttpClient.Builder()
.addAuthentication(null, loginModel.credentials!!)
.build()
}
@After
fun stopServer() {
server.shutdown()
}
@Test
@SmallTest
fun testRememberIfAddressBookOrHomeset() {
// recognize home set
var info = ServiceInfo()
DavResource(client.okHttpClient, server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL))
.propfind(0, AddressbookHomeSet.NAME) { response, _ ->
finder.scanCardDavResponse(response, info)
}
assertEquals(0, info.collections.size)
assertEquals(1, info.homeSets.size)
assertEquals(server.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK_HOMESET/"), info.homeSets.first())
// recognize address book
info = ServiceInfo()
DavResource(client.okHttpClient, server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK))
.propfind(0, ResourceType.NAME) { response, _ ->
finder.scanCardDavResponse(response, info)
}
assertEquals(1, info.collections.size)
assertEquals(server.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/"), info.collections.keys.first())
assertEquals(0, info.homeSets.size)
}
@Test
fun testProvidesService() {
assertFalse(finder.providesService(server.url(PATH_NO_DAV), DavResourceFinder.Service.CALDAV))
assertFalse(finder.providesService(server.url(PATH_NO_DAV), DavResourceFinder.Service.CARDDAV))
assertTrue(finder.providesService(server.url(PATH_CALDAV), DavResourceFinder.Service.CALDAV))
assertFalse(finder.providesService(server.url(PATH_CALDAV), DavResourceFinder.Service.CARDDAV))
assertTrue(finder.providesService(server.url(PATH_CARDDAV), DavResourceFinder.Service.CARDDAV))
assertFalse(finder.providesService(server.url(PATH_CARDDAV), DavResourceFinder.Service.CALDAV))
assertTrue(finder.providesService(server.url(PATH_CALDAV_AND_CARDDAV), DavResourceFinder.Service.CALDAV))
assertTrue(finder.providesService(server.url(PATH_CALDAV_AND_CARDDAV), DavResourceFinder.Service.CARDDAV))
}
@Test
fun testGetCurrentUserPrincipal() {
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_NO_DAV), DavResourceFinder.Service.CALDAV))
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_NO_DAV), DavResourceFinder.Service.CARDDAV))
assertEquals(
server.url(PATH_CALDAV + SUBPATH_PRINCIPAL),
finder.getCurrentUserPrincipal(server.url(PATH_CALDAV), DavResourceFinder.Service.CALDAV)
)
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_CALDAV), DavResourceFinder.Service.CARDDAV))
assertEquals(
server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL),
finder.getCurrentUserPrincipal(server.url(PATH_CARDDAV), DavResourceFinder.Service.CARDDAV)
)
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_CARDDAV), DavResourceFinder.Service.CALDAV))
}
@Test
fun testQueryEmailAddress() {
var info = ServiceInfo()
assertArrayEquals(
arrayOf("email1@example.com", "email2@example.com"),
finder.queryEmailAddress(server.url(PATH_CALDAV + SUBPATH_PRINCIPAL)).toTypedArray()
)
assertTrue(finder.queryEmailAddress(server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL)).isEmpty())
}
// mock server
class TestDispatcher: Dispatcher() {
override fun dispatch(request: RecordedRequest): MockResponse {
if (!checkAuth(request)) {
val authenticate = MockResponse().setResponseCode(401)
authenticate.setHeader("WWW-Authenticate", "Basic realm=\"test\"")
return authenticate
}
val path = request.path!!
if (request.method.equals("OPTIONS", true)) {
val dav = when {
path.startsWith(PATH_CALDAV) -> "calendar-access"
path.startsWith(PATH_CARDDAV) -> "addressbook"
path.startsWith(PATH_CALDAV_AND_CARDDAV) -> "calendar-access, addressbook"
else -> null
}
val response = MockResponse().setResponseCode(200)
if (dav != null)
response.addHeader("DAV", dav)
return response
} else if (request.method.equals("PROPFIND", true)) {
val props: String?
when (path) {
PATH_CALDAV,
PATH_CARDDAV ->
props = "<current-user-principal><href>$path$SUBPATH_PRINCIPAL</href></current-user-principal>"
PATH_CARDDAV + SUBPATH_PRINCIPAL ->
props = "<CARD:addressbook-home-set>" +
" <href>$PATH_CARDDAV$SUBPATH_ADDRESSBOOK_HOMESET</href>" +
"</CARD:addressbook-home-set>"
PATH_CARDDAV + SUBPATH_ADDRESSBOOK ->
props = "<resourcetype>" +
" <collection/>" +
" <CARD:addressbook/>" +
"</resourcetype>"
PATH_CALDAV + SUBPATH_PRINCIPAL ->
props = "<CAL:calendar-user-address-set>" +
" <href>urn:unknown-entry</href>" +
" <href>mailto:email1@example.com</href>" +
" <href>mailto:email2@example.com</href>" +
"</CAL:calendar-user-address-set>"
else -> props = null
}
Logger.log.info("Sending props: $props")
return MockResponse()
.setResponseCode(207)
.setBody("<multistatus xmlns='DAV:' xmlns:CARD='urn:ietf:params:xml:ns:carddav' xmlns:CAL='urn:ietf:params:xml:ns:caldav'>" +
"<response>" +
" <href>${request.path}</href>" +
" <propstat><prop>$props</prop></propstat>" +
"</response>" +
"</multistatus>")
}
return MockResponse().setResponseCode(404)
}
private fun checkAuth(rq: RecordedRequest) =
rq.getHeader("Authorization") == "Basic bW9jazoxMjM0NQ=="
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

@@ -0,0 +1,3 @@
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="at.bitfire.davdroid.SyncManagerTest"
android:label="Test Account" />
@@ -1,47 +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.MenuItem
import at.bitfire.davdroid.App
import at.bitfire.davdroid.BuildConfig
import at.bitfire.davdroid.R
class DefaultAccountsDrawerHandler: IAccountsDrawerHandler {
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_twitter ->
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://twitter.com/davdroidapp")))
R.id.nav_website ->
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(activity.getString(R.string.homepage_url))))
R.id.nav_faq ->
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(activity.getString(R.string.navigation_drawer_faq_url))))
R.id.nav_forums ->
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(activity.getString(R.string.homepage_url))
.buildUpon().appendEncodedPath("forums/").build()))
R.id.nav_donate ->
if (BuildConfig.FLAVOR != App.FLAVOR_GOOGLE_PLAY)
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(activity.getString(R.string.homepage_url))
.buildUpon().appendEncodedPath("donate/").build()))
else ->
return false
}
return true
}
}
@@ -1,165 +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.app.Fragment
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CompoundButton
import at.bitfire.dav4android.Constants
import at.bitfire.davdroid.R
import kotlinx.android.synthetic.standard.login_credentials_fragment.view.*
import java.net.IDN
import java.net.URI
import java.net.URISyntaxException
import java.util.logging.Level
class DefaultLoginCredentialsFragment: Fragment(), CompoundButton.OnCheckedChangeListener {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
val v = inflater.inflate(R.layout.login_credentials_fragment, container, false)
if (savedInstanceState == null) {
// first call
activity?.intent?.let {
// we've got initial login data
val url = it.getStringExtra(LoginActivity.EXTRA_URL)
val username = it.getStringExtra(LoginActivity.EXTRA_USERNAME)
val password = it.getStringExtra(LoginActivity.EXTRA_PASSWORD)
if (url != null) {
v.login_type_url.isChecked = true
v.base_url.setText(url)
v.user_name.setText(username)
v.url_password.setText(password)
} else {
v.login_type_email.isChecked = true
v.email_address.setText(username)
v.email_password.setText(password)
}
}
}
v.login.setOnClickListener({ _ ->
validateLoginData()?.let { credentials ->
DetectConfigurationFragment.newInstance(credentials).show(fragmentManager, null)
}
})
// initialize to Login by email
onCheckedChanged(v)
v.login_type_email.setOnCheckedChangeListener(this)
v.login_type_url.setOnCheckedChangeListener(this)
return v
}
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
onCheckedChanged(view)
}
private fun onCheckedChanged(v: View) {
val loginByEmail = !v.login_type_url.isChecked
v.login_type_email_details.visibility = if (loginByEmail) View.VISIBLE else View.GONE
v.login_type_url_details.visibility = if (loginByEmail) View.GONE else View.VISIBLE
(if (loginByEmail) v.email_address else v.base_url).requestFocus()
}
private fun validateLoginData(): LoginCredentials? {
if (view.login_type_email.isChecked) {
var uri: URI? = null
var valid = true
val email = view.email_address.text.toString()
if (!email.matches(Regex(".+@.+"))) {
view.email_address.error = getString(R.string.login_email_address_error)
valid = false
} else
try {
uri = URI("mailto", email, null)
} catch(e: URISyntaxException) {
view.email_address.error = e.localizedMessage
valid = false
}
val password = view.email_password.getText().toString()
if (password.isEmpty()) {
view.email_password.setError(getString(R.string.login_password_required))
valid = false
}
return if (valid && uri != null)
LoginCredentials(uri, email, password)
else
null
} else if (view.login_type_url.isChecked) {
var uri: URI? = null
var valid = true
val baseUrl = Uri.parse(view.base_url.text.toString())
val scheme = baseUrl.scheme
if (scheme.equals("http", true) || scheme.equals("https", true)) {
var host = baseUrl.host
if (host.isNullOrBlank()) {
view.base_url.error = getString(R.string.login_url_host_name_required)
valid = false
} else
try {
host = IDN.toASCII(host)
} catch(e: IllegalArgumentException) {
Constants.log.log(Level.WARNING, "Host name not conforming to RFC 3490", e)
}
val path = baseUrl.encodedPath
val port = baseUrl.port
try {
uri = URI(baseUrl.scheme, null, host, port, path, null, null)
} catch(e: URISyntaxException) {
view.base_url.error = e.localizedMessage
valid = false
}
} else {
view.base_url.error = getString(R.string.login_url_must_be_http_or_https)
valid = false
}
val userName = view.user_name.text.toString()
if (userName.isBlank()) {
view.user_name.error = getString(R.string.login_user_name_required)
valid = false
}
val password = view.url_password.getText().toString()
if (password.isEmpty()) {
view.url_password.setError(getString(R.string.login_password_required))
valid = false
}
return if (valid && uri != null)
LoginCredentials(uri, userName, password)
else
null
}
return null
}
class Factory: ILoginCredentialsFragment {
override fun getFragment() = DefaultLoginCredentialsFragment()
}
}
@@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright © 2013 2016 Ricki Hirner (bitfire web engineering).
~ All rights reserved. This program and the accompanying materials
~ are made available under the terms of the GNU Public License v3.0
~ which accompanies this distribution, and is available at
~ http://www.gnu.org/licenses/gpl.html
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- We don't want the keyboard up when the user arrives in this initial screen -->
<View android:layout_height="0dp"
android:layout_width="0dp"
android:focusable="true"
android:focusableInTouchMode="true"
android:contentDescription="@null"
android:importantForAccessibility="no" tools:ignore="UnusedAttribute">
<requestFocus/>
</View>
<ScrollView android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_margin="@dimen/activity_margin">
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:animateLayoutChanges="true">
<RadioButton
android:id="@+id/login_type_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/login_type_email"
android:paddingLeft="14dp" tools:ignore="RtlSymmetry"
style="@style/login_type_headline"/>
<LinearLayout
android:id="@+id/login_type_email_details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:id="@+id/email_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/login_email_address"
android:inputType="textEmailAddress"/>
<at.bitfire.davdroid.ui.widget.EditPassword
android:id="@+id/email_password"
android:hint="@string/login_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
<RadioButton
android:id="@+id/login_type_url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/login_type_url"
android:layout_marginTop="16dp"
android:paddingLeft="14dp" tools:ignore="RtlSymmetry"
style="@style/login_type_headline"/>
<LinearLayout
android:id="@+id/login_type_url_details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:id="@+id/base_url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/login_base_url"
android:inputType="textUri"/>
<EditText
android:id="@+id/user_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/login_user_name"
android:inputType="textEmailAddress"/>
<at.bitfire.davdroid.ui.widget.EditPassword
android:id="@+id/url_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/login_password"/>
</LinearLayout>
</RadioGroup>
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/stepper_nav_bar">
<Space
android:layout_width="0dp"
android:layout_weight="1"
style="@style/stepper_nav_button"/>
<Button
android:id="@+id/login"
android:layout_width="0dp"
android:layout_weight="1"
android:text="@string/login_login"
style="@style/stepper_nav_button"/>
</LinearLayout>
</LinearLayout>
@@ -1,22 +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">Ajuda</string>
<!--startup dialogs-->
<!--AboutActivity-->
<!--global settings-->
<!--AccountsActivity-->
<!--DavService-->
<!--AppSettingsActivity-->
<!--AccountActivity-->
<!--PermissionsActivity-->
<!--AddAccountActivity-->
<string name="login_type_email">Entra amb una adreça de correu electrònic</string>
<string name="login_type_url">Entra amb una URL i un nom d\'usuari</string>
<!--AccountSettingsActivity-->
<!--collection management-->
<!--ExceptionInfoFragment-->
<!--sync errors and DebugInfoActivity-->
<!--cert4android-->
</resources>
-242
View File
@@ -1,242 +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">Optimalizace využití baterie</string>
<string name="startup_battery_optimization_message">Android může po několika dnech vypnout/prodloužit interval synchronizování DAVdroid. Chcete-li tomuto zabránit, vypněte optimalizaci baterie.</string>
<string name="startup_battery_optimization_disable">Vypnout pro DAVdroid</string>
<string name="startup_dont_show_again">Již nezobrazovat</string>
<string name="startup_development_version_give_feedback">Dát zpětnou vazbu</string>
<string name="startup_donate">Open Source informace</string>
<string name="startup_donate_message">Jsme velice rádi že používáte DAVdroid, software s otevřeným zdrojovým kódem (GPLv3). Vývoj této aplikace je náročný a trval již několik tisíc hodin, velice nás potěší přispějete-li na jeho vývoj.</string>
<string name="startup_donate_now">Zobrazit stránku pro obdarování</string>
<string name="startup_donate_later">Možná později</string>
<string name="startup_google_play_accounts_removed">Informace o chybě DRM Obchodu Play</string>
<string name="startup_google_play_accounts_removed_message">Za určitých podmínek může dojít po restartu nebo aktualizaci aplikace DAVdroid k vymazání účtů kvůli chybě DRM Obchodu Play. Pokud jste postiženi touto chybou (ale pouze v tomto případě), nainstalujte prosím z Obchodu Play aplikaci \"DAVdroid JB Workaround\".</string>
<string name="startup_google_play_accounts_removed_more_info">Více informací</string>
<string name="startup_opentasks_not_installed">OpenTasks není nainstalován</string>
<string name="startup_opentasks_not_installed_message">Aplikace OpenTasks není dostupná, proto nebude DAVdroid moci synchronizovat seznam úkolů.</string>
<string name="startup_opentasks_reinstall_davdroid">Po instalaci OpenTasks musíte PŘEINSTALOVAT DAVdroid a přidat znovu své účty (Android chyba).</string>
<string name="startup_opentasks_not_installed_install">Nainstalovat OpenTasks</string>
<!--AboutActivity-->
<string name="about_license_terms">Licenční podmínky</string>
<string name="about_license_info_no_warranty">Tento program je distribuován BEZ JAKÉKOLIV ZÁRUKY. Je to volně dostupný software a lze jej za určitých podmínek dále distribuovat.</string>
<!--global settings-->
<string name="logging_davdroid_file_logging">DAVdroid logování do souboru</string>
<string name="logging_to_external_storage">Logování do externího úložiště: %s</string>
<string name="logging_to_external_storage_warning">Smazat logy jak nejdříve možno!</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>
<!--PermissionsActivity-->
<string name="permissions_title">DAVdroid oprávnění</string>
<string name="permissions_calendar">Oprávnění pro kalendáře</string>
<string name="permissions_calendar_details">Pro synchronizaci CalDAV událostí s místním kalendářem potřebuje DAVdroid oprávnění přistupovat ke kalendářům.</string>
<string name="permissions_calendar_request">Vyžádat oprávnění kalendáře</string>
<string name="permissions_contacts">Oprávnění pro kontakty</string>
<string name="permissions_contacts_details">Pro synchronizaci CardDAV adresářů s místními kontakty potřebuje DAVdroid oprávnění přistupovat ke kontaktům.</string>
<string name="permissions_contacts_request">Vyžádat oprávnění kontaktů</string>
<string name="permissions_opentasks">Oprávnění pro OpenTasks</string>
<string name="permissions_opentasks_details">Pro synchronizaci CalDAV událostí s místním seznamem úkolů potřebuje DAVdroid oprávnění přistupovat k OpenTasks.</string>
<string name="permissions_opentasks_request">Vyžádat oprávnění OpenTasks</string>
<!--AddAccountActivity-->
<string name="login_title">Přidat účet</string>
<string name="login_type_email">Přihlášení s emailovou adresou</string>
<string name="login_email_address">Emailová adresa</string>
<string name="login_email_address_error">Vyžadován platný email</string>
<string name="login_password">Heslo</string>
<string name="login_password_required">Vyžadováno heslo</string>
<string name="login_type_url">Přihlášení s URL a uživatelským jménem</string>
<string name="login_url_must_be_http_or_https">URL musí začínat na http(s)://</string>
<string name="login_url_host_name_required">Vyžadováno hostname</string>
<string name="login_user_name">Uživatelské jméno</string>
<string name="login_user_name_required">Vyžadováno uživatelské jméno</string>
<string name="login_base_url">Základní URL</string>
<string name="login_login">Login</string>
<string name="login_back">Zpět</string>
<string name="login_create_account">Vytvořit účet</string>
<string name="login_account_name">Jméno účtu</string>
<string name="login_account_name_info">Pro jméno účtu použijte svou emailovou adresu, protože Android bude brát jméno účtu jako údaj pro ORGANIZÁTORA vytvořených událostí. Nelze mít dva účty stejného jména.</string>
<string name="login_account_contact_group_method">Metoda seskupování kontaktů:</string>
<string name="login_account_name_required">Vyžadováno jméno účtu</string>
<string name="login_account_not_created">Účet nelze vytvořit</string>
<string name="login_configuration_detection">Vyhledání konfigurace</string>
<string name="login_querying_server">Chvíli strpení, probíhá dotazování serveru...</string>
<string name="login_no_caldav_carddav">Nelze nalézt službu CalDAV nebo CardDAV.</string>
<string name="login_view_logs">Prohlížet logy</string>
<!--AccountSettingsActivity-->
<string name="settings_title">Nastavení: %s</string>
<string name="settings_authentication">Ověření</string>
<string name="settings_username">Uživatelské jméno</string>
<string name="settings_enter_username">Zadat uživatelské jméno</string>
<string name="settings_password">Heslo</string>
<string name="settings_password_summary">Aktualizovat heslo dle svého serveru.</string>
<string name="settings_enter_password">Vložit své heslo:</string>
<string name="settings_sync">Synchronizace</string>
<string name="settings_sync_interval_contacts">Interval synchronizace kontaktů</string>
<string name="settings_sync_summary_manually">Pouze manuálně</string>
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Každých %d minut a ihned při lokálních změnách</string>
<string name="settings_sync_interval_calendars">Interval synchronizace kalendáře</string>
<string name="settings_sync_interval_tasks">Interval synchronizace úkolů</string>
<string-array name="settings_sync_interval_seconds">
<item>-1</item>
<item>300</item>
<item>600</item>
<item>900</item>
<item>3600</item>
<item>7200</item>
<item>14400</item>
<item>86400</item>
</string-array>
<string-array name="settings_sync_interval_names">
<item>Pouze manuálně</item>
<item>Každých 5 minut</item>
<item>Každých 10 minut</item>
<item>Každých 15 minut</item>
<item>Každou hodinu</item>
<item>Každé 2 hodiny</item>
<item>Každé 4 hodiny</item>
<item>Jednou za den</item>
</string-array>
<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_sync_wifi_only_ssid">Omezení WiFi SSID</string>
<string name="settings_sync_wifi_only_ssid_on">Synchronizace pouze přes %s</string>
<string name="settings_sync_wifi_only_ssid_off">Použít všechna WiFi připojení</string>
<string name="settings_sync_wifi_only_ssid_message">Zadejte jméno WiFi sítě (SSID) pro omezení synchronizace na tutu síť, nebo ponechte prázdné pro použití všech WiFi připojení.</string>
<string name="settings_carddav">CardDAV</string>
<string name="settings_contact_group_method">Metoda seskupování kontaktů</string>
<string-array name="settings_contact_group_method_values">
<item>GROUP_VCARDS</item>
<item>CATEGORIES</item>
</string-array>
<string-array name="settings_contact_group_method_entries">
<item>Skupiny jsou oddělené soubory VCard</item>
<item>Skupiny jsou kategorie na kontakt</item>
</string-array>
<string name="settings_caldav">CalDAV</string>
<string name="settings_sync_time_range_past">Časový limit pro staré události</string>
<string name="settings_sync_time_range_past_none">Synchronizovat všechny události</string>
<plurals name="settings_sync_time_range_past_days">
<item quantity="one">Ignorovat události starší než 1 den</item>
<item quantity="few">Ignorovat události starší než %d dny</item>
<item quantity="other">Ignorovat události starší než %d dnů</item>
</plurals>
<string name="settings_sync_time_range_past_message">Události z minulosti starší než vyznačený počet dnů budou ignorovány (lze zadat 0). Ponechte prázdné pro synchronizaci všech událostí.</string>
<string name="settings_manage_calendar_colors">Spravovat barvy kalendářů</string>
<string name="settings_manage_calendar_colors_on">Barvy kalendářů spravuje DAVdroid</string>
<string name="settings_manage_calendar_colors_off">Barvy kalendářů nespravuje DAVdroid</string>
<!--collection management-->
<string name="create_addressbook">Vytvořit adresář</string>
<string name="create_addressbook_display_name_hint">Můj adresář</string>
<string name="create_calendar">Vytvořit CalDAV sbírku</string>
<string name="create_calendar_display_name_hint">Můj kalendář</string>
<string name="create_calendar_time_zone">Časová zóna:</string>
<string name="create_calendar_type">Typ sbírky:</string>
<string name="create_calendar_type_only_events">Kalendář (pouze události)</string>
<string name="create_calendar_type_only_tasks">Seznam úkolů (pouze úkoly)</string>
<string name="create_calendar_type_events_and_tasks">Kombinovaná (události a úkoly)</string>
<string name="create_collection_color">Nastavit barvu sbírky</string>
<string name="create_collection_creating">Vytváření sbírky</string>
<string name="create_collection_display_name">Zobrazit jméno (nadpis) této sbírky:</string>
<string name="create_collection_display_name_required">Nadpis je vyžadován</string>
<string name="create_collection_description">Popis (volitelný):</string>
<string name="create_collection_home_set">Domácí sbírka:</string>
<string name="create_collection_create">Vytvořit</string>
<string name="delete_collection">Smazat sbírku</string>
<string name="delete_collection_confirm_title">Jste si jisti?</string>
<string name="delete_collection_confirm_warning">Tato sbírka (%s) a všechna její data budou odstraněna ze serveru.</string>
<string name="delete_collection_deleting_collection">Mazání sbírky</string>
<!--ExceptionInfoFragment-->
<string name="exception">Došlo k chybě.</string>
<string name="exception_httpexception">Došlo k HTTP chybě.</string>
<string name="exception_ioexception">Došlo k I/O chybě.</string>
<string name="exception_show_details">Zobrazit detaily</string>
<!--sync errors and DebugInfoActivity-->
<string name="debug_info_title">Ladící informace</string>
<string name="sync_error_permissions">DAVdroid oprávnění</string>
<string name="sync_error_permissions_text">Vyžadována dodatečná oprávnění</string>
<string name="sync_error_calendar">Synchronizace kalendáře selhala (%s)</string>
<string name="sync_error_contacts">Synchronizace adresáře selhala (%s)</string>
<string name="sync_error_tasks">Synchronizace úkolu selhala (%s)</string>
<string name="sync_error">Chyba při %s</string>
<string name="sync_error_http_dav">Chyba serveru při %s</string>
<string name="sync_error_local_storage">Chyba databáze při %s</string>
<string-array name="sync_error_phases">
<item>příprava synchronizace</item>
<item>dotazování možností</item>
<item>zpracovávání místně smazaných záznamů</item>
<item>příprava vytvořených/upravených záznamů</item>
<item>nahrávání vytvořených/upravených záznamů</item>
<item>kontrola stavu synchronizace</item>
<item>výpis místních záznamů</item>
<item>výpis vzdálených záznamů</item>
<item>porovnání místních/vzdálených záznamů</item>
<item>stahování vzdálených záznamů</item>
<item>uzavírání procesu</item>
<item>ukládání stavu synchronizace</item>
</string-array>
<string name="sync_error_unauthorized">Chybné uživatelské jméno/heslo</string>
<!--cert4android-->
<string name="certificate_notification_connection_security">DAVdroid: Zabezpečení připojení</string>
<string name="trust_certificate_unknown_certificate_found">DAVdroid nalezl neznámý certifikát. Chcete mu důvěřovat?</string>
</resources>
-245
View File
@@ -1,245 +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 Addressebog</string>
<string name="address_books_authority_title">Adressebøger</string>
<string name="help">Hjælp</string>
<string name="manage_accounts">Administrer konti</string>
<string name="please_wait">Vent venligst ...</string>
<string name="send">Send</string>
<!--startup dialogs-->
<string name="startup_battery_optimization">Batterioptimering</string>
<string name="startup_battery_optimization_message">Android kan deaktivere/reducere DAVDroid synkronisering efter et par dage. For at undgå dette, slå batterioptimering fra.</string>
<string name="startup_battery_optimization_disable">Deaktiver DAVdroid</string>
<string name="startup_dont_show_again">Vis ikke igen</string>
<string name="startup_development_version_give_feedback">Giv feedback</string>
<string name="startup_donate">Open-Source Information</string>
<string name="startup_donate_message">Det glæder os, at du bruger DAVdroid, som er open source-software (GPLv3). Det er hårdt arbejde at udvikle DAVdroid og taget tusindvis af arbejdstimer, så overvej at donere til projektet.</string>
<string name="startup_donate_now">Vis donationsside</string>
<string name="startup_donate_later">Måske senere</string>
<string name="startup_google_play_accounts_removed">Play Store DRM-fejl: Information</string>
<string name="startup_google_play_accounts_removed_message">Under visse tekniske omstændigheder kan DRM fra Play Store bevirke, at alle DAVdroid-konti er væk efter en genstart eller opgradering af DAVdroid. Hvis du er udsat for dette problem (og ellers ikke), opfordres du til at installere DAVDroid JB Workaround\" fra Play Store.</string>
<string name="startup_google_play_accounts_removed_more_info">Yderligere oplysninger</string>
<string name="startup_opentasks_not_installed">OpenTasks ikke installeret</string>
<string name="startup_opentasks_not_installed_message">OpenTasks er ikke til rådighedm så DAVdroid vil ikke kunne synkronisere opgavelister.</string>
<string name="startup_opentasks_reinstall_davdroid">Efter at have installeret OpenTasks, vil du være nødt til at GENINSTALLERE DAVdroid og dine konti igen (en fejl i Android).</string>
<string name="startup_opentasks_not_installed_install">Installer OpenTasks</string>
<!--AboutActivity-->
<string name="about_license_terms">Licensforhold</string>
<string name="about_license_info_no_warranty">Dette program leveres ABSOLUT UDEN GARANTI. Det er fri software, og du er velkommen til at videredistribuere det under visse betingelse.</string>
<!--global settings-->
<string name="logging_davdroid_file_logging">DAVdroid fil-logning</string>
<string name="logging_to_external_storage">Logger til eksternt datalager: %s</string>
<string name="logging_to_external_storage_warning">Slet logfiler hurtigst muligt!</string>
<string name="logging_couldnt_create_file">Kunne ikke oprette ekstern logfil: %s</string>
<string name="logging_no_external_storage">Eksternt lager ikke funder</string>
<!--AccountsActivity-->
<string name="navigation_drawer_open">Åbn navigationsvindue</string>
<string name="navigation_drawer_close">Luk navigationsvindue</string>
<string name="navigation_drawer_subtitle">CalDAV/CardDAV Sync-adapter</string>
<string name="navigation_drawer_about">Om / Licens</string>
<string name="navigation_drawer_settings">Indstillinger</string>
<string name="navigation_drawer_news_updates">Nyheder &amp; opdateringer</string>
<string name="navigation_drawer_external_links">Eksterne links</string>
<string name="navigation_drawer_website">Hjemmeside</string>
<string name="navigation_drawer_faq">FAQ</string>
<string name="navigation_drawer_donate">Donation</string>
<string name="account_list_empty">Velkommen til DAVdroid!\n\nDu kan nu tilføje en CaDAV/CardDAV-konto.</string>
<string name="accounts_global_sync_disabled">Automatisk synkronisering på tværs af systemet er deaktiveret</string>
<string name="accounts_global_sync_enable">Aktiver</string>
<!--DavService-->
<string name="dav_service_refresh_failed">Registrering af tjeneste kunne ikke foretages</string>
<string name="dav_service_refresh_couldnt_refresh">Kunne opdatere liste over sæt</string>
<!--AppSettingsActivity-->
<string name="app_settings">Indstillinger</string>
<string name="app_settings_user_interface">Brugerflade</string>
<string name="app_settings_reset_hints">Nulstil vejledende popups</string>
<string name="app_settings_reset_hints_summary">Genaktiverer hjælp, som er blevet lukket tidligere</string>
<string name="app_settings_reset_hints_success">Al vejledning vil blive vist igen</string>
<string name="app_settings_connection">Forbindelse</string>
<string name="app_settings_override_proxy">Tilsidesæt proxyindstillinger</string>
<string name="app_settings_override_proxy_on">Brug brugerdefinerede proxyindstillinger</string>
<string name="app_settings_override_proxy_off">Brug systemstandard proxyindstillinger</string>
<string name="app_settings_override_proxy_host">HTTP proxy værtsnavn</string>
<string name="app_settings_override_proxy_port">HTTP proxy port</string>
<string name="app_settings_security">Sikkerhed</string>
<string name="app_settings_distrust_system_certs">Stol ikke på systemcertifikater</string>
<string name="app_settings_distrust_system_certs_on">System og brugertilføjede CA\'er vil ikke blive betroet</string>
<string name="app_settings_distrust_system_certs_off">System og brugertilføjede CA\'er vil blive betroet (anbefalet)</string>
<string name="app_settings_reset_certificates">Nulstil (ikke-)betroede certifikater</string>
<string name="app_settings_reset_certificates_summary">Nulstiller tilliden til brugerdefinerede certifikater</string>
<string name="app_settings_reset_certificates_success">Alle brugerdefinerede certifikater er blevet rydet</string>
<string name="app_settings_debug">Debugging</string>
<string name="app_settings_log_to_external_storage">Log til ekstern fil</string>
<string name="app_settings_log_to_external_storage_on">Logger til eksternt lager (hvis muligt)</string>
<string name="app_settings_log_to_external_storage_off">Ekstern logning deaktiveret</string>
<string name="app_settings_show_debug_info">Vis debug-info</string>
<string name="app_settings_show_debug_info_details">Vis/del software og opsætningsoplysninger</string>
<!--AccountActivity-->
<string name="account_synchronize_now">Synkroniser nu</string>
<string name="account_synchronizing_now">Synkroniserer nu</string>
<string name="account_settings">Opsætning af konti</string>
<string name="account_rename">Omdøb konto</string>
<string name="account_rename_new_name">Lokaldata der ikke er gemt kan gå tabt. Eftersynkronisering er krævet efter omdøbning. Nyt kontonavn: </string>
<string name="account_rename_rename">Omdøb</string>
<string name="account_delete">Slet konto</string>
<string name="account_delete_confirmation_title">Ønsker du at slette konto?</string>
<string name="account_delete_confirmation_text">Alle lokale kopier af addessebøger, kalendere og opgavelister vil blive slettet.</string>
<string name="account_refresh_address_book_list">Opdater adressebogslister</string>
<string name="account_create_new_address_book">Opret ny adressebog</string>
<string name="account_refresh_calendar_list">Opdater kalenderliste</string>
<string name="account_create_new_calendar">Opret ny kalender</string>
<!--PermissionsActivity-->
<string name="permissions_title">DAVdroid adgangsrettigheder</string>
<string name="permissions_calendar">Kalenderadgange</string>
<string name="permissions_calendar_details">For at synkronisere CalDAV-begivenheder med dine lokale kalendere, skal DAVdroid have adgang til dine kalendere.</string>
<string name="permissions_calendar_request">Anmod om kalenderadgang</string>
<string name="permissions_contacts">Kontakter: Adgangsrettigheder</string>
<string name="permissions_contacts_details">DAVdroid er nødt til at have adgang til dine kontakter, hvis CardDAV-adressebøger skal kunne synkronisere med dine kontakter.</string>
<string name="permissions_contacts_request">Anmod om adgang til kontakter</string>
<string name="permissions_opentasks">OpenTasks: Adgangsrettigheder</string>
<string name="permissions_opentasks_details">DAVdroid er nødt til at have adgang til OpenTasks, hvis CalDAV-opgaver skal kunne synkronisere med dine lokale opgavelister.</string>
<string name="permissions_opentasks_request">Anmod om adgang til OpenTasks</string>
<!--AddAccountActivity-->
<string name="login_title">Tilføj konto</string>
<string name="login_type_email">Log ind med emailadresse</string>
<string name="login_email_address">Emailadresse</string>
<string name="login_email_address_error">Gyldig emailadresse påkrævet</string>
<string name="login_password">Adgangskode</string>
<string name="login_password_required">Adgangskode påkrævet</string>
<string name="login_type_url">Log ind med URL og brugernavn</string>
<string name="login_url_must_be_http_or_https">URL skal begynde med http(s)://</string>
<string name="login_url_host_name_required">Værtsnavn påkrævet</string>
<string name="login_user_name">Brugernavn</string>
<string name="login_user_name_required">Brugernavn påkrævet</string>
<string name="login_base_url">Basis-URL</string>
<string name="login_login">Login</string>
<string name="login_back">Tilbage</string>
<string name="login_create_account">Opret konto</string>
<string name="login_account_name">Kontonavn</string>
<string name="login_account_name_info">Brug emailadressen som kontonavn, for Android vil bruge kontonavnet til ORGANIZER-feltet for aktiviteter, som du opretter. Du kan ikke have to konti med samme navn.</string>
<string name="login_account_contact_group_method">Gruppering af kontakter:</string>
<string name="login_account_name_required">Kontonavn påkrævet</string>
<string name="login_account_not_created">Konto kunne ikke oprettes</string>
<string name="login_configuration_detection">Check konfiguration</string>
<string name="login_querying_server">Vent, forespørger hos serveren...</string>
<string name="login_no_caldav_carddav">Kunne ikke finde CalDAV- eller CardDAV-tjeneste.</string>
<string name="login_view_logs">Vis logs.</string>
<!--AccountSettingsActivity-->
<string name="settings_title">Indstillinger: %s</string>
<string name="settings_authentication">Adgangsgodkendelse</string>
<string name="settings_username">Brugernavn</string>
<string name="settings_enter_username">Indtast brugernavn:</string>
<string name="settings_password">Adgangskode</string>
<string name="settings_password_summary">Opdater adgangskoden, så den svarer til din server.</string>
<string name="settings_enter_password">Indtast adgangskode:</string>
<string name="settings_sync">Synkronisering</string>
<string name="settings_sync_interval_contacts">Synkroniseringsinterval for kontakter</string>
<string name="settings_sync_summary_manually">Kun manuelt</string>
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Hver %d minutter + øjeblikkeligt ved lokale ændringer</string>
<string name="settings_sync_interval_calendars">Synkroniseringsinterval for kalender</string>
<string name="settings_sync_interval_tasks">Synkroniseringsinterval for opgaver</string>
<string-array name="settings_sync_interval_seconds">
<item>-1</item>
<item>300</item>
<item>600</item>
<item>900</item>
<item>3600</item>
<item>7200</item>
<item>14400</item>
<item>86400</item>
</string-array>
<string-array name="settings_sync_interval_names">
<item>Kun manuelt</item>
<item>Hver 5. minut</item>
<item>Hver 10. minut</item>
<item>Hver 15. minut</item>
<item>En gang i timen</item>
<item>En gang hver 2. time</item>
<item>En gang 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_ssid">Begræsning til WiFi-SSID</string>
<string name="settings_sync_wifi_only_ssid_on">Vil kun blive synkroniseret over %s</string>
<string name="settings_sync_wifi_only_ssid_off">Alle WiFi-forbindelse vil kunne bruges</string>
<string name="settings_sync_wifi_only_ssid_message">Indtast navnet på et WiFi-netværk (SSID) for at begrænse synkronisering til dette netværk, eller efterlad feltet blank for at acceptere alle WiFi-forbindelser.</string>
<string name="settings_carddav">CardDAV</string>
<string name="settings_contact_group_method">Gruppering af kontakter</string>
<string-array name="settings_contact_group_method_values">
<item>GROUP_VCARDS</item>
<item>CATEGORIES</item>
</string-array>
<string-array name="settings_contact_group_method_entries">
<item>Grupper er særskilte VCards</item>
<item>Grupper er kategorier pr. kontakt</item>
</string-array>
<string name="settings_caldav">CalDAV</string>
<string name="settings_sync_time_range_past">Tidsafgrænsning for tidligere begivenheder</string>
<string name="settings_sync_time_range_past_none">Alle begivenheder vil blive synkroniseret</string>
<plurals name="settings_sync_time_range_past_days">
<item quantity="one">Begivenheder ældre end en dag vil blive ignoreret</item>
<item quantity="other">Begivenheder, der er mere end %d dage gamle, vil blive ignoreret</item>
</plurals>
<string name="settings_sync_time_range_past_message">Begivenheder, som er mere end dette antal dage gamle vil blive ignoreret (kan også være 0). Hvis feltet ikke er udfyldt, vil alle begivenheder blive synkroniseret.</string>
<string name="settings_manage_calendar_colors">Administrer farver for kalender</string>
<string name="settings_manage_calendar_colors_on">Kalenderfarver administreres af DAVdroid</string>
<string name="settings_manage_calendar_colors_off">Kalenderfarver sættes ikke fra DAVdroid</string>
<!--collection management-->
<string name="create_addressbook">Opret adressebog</string>
<string name="create_addressbook_display_name_hint">Min adressebog</string>
<string name="create_calendar">Opret CalDAV-sæt</string>
<string name="create_calendar_display_name_hint">Min kalender</string>
<string name="create_calendar_time_zone">Tidszone:</string>
<string name="create_calendar_type">Type af sæt:</string>
<string name="create_calendar_type_only_events">Kalender (kun begivenheder)</string>
<string name="create_calendar_type_only_tasks">Opgaveliste (kun opgaver)</string>
<string name="create_calendar_type_events_and_tasks">Kombineret (begivenheder og opgaver)</string>
<string name="create_collection_color">Sæt en farve på sættet</string>
<string name="create_collection_creating">Opretter sæt</string>
<string name="create_collection_display_name">Navn (titel) på dette sæt:</string>
<string name="create_collection_display_name_required">En titel er påkrævet</string>
<string name="create_collection_description">Beskrivelse (valgfri):</string>
<string name="create_collection_home_set">Home set:</string>
<string name="create_collection_create">Opret</string>
<string name="delete_collection">Slet sæt</string>
<string name="delete_collection_confirm_title">Er du sikker?</string>
<string name="delete_collection_confirm_warning">DAV-sættet (%s) og dets data vil blive fjernet fra serveren.</string>
<string name="delete_collection_deleting_collection">Sletter CalDAV-sæt</string>
<!--ExceptionInfoFragment-->
<string name="exception">Der er opstået en fejl.</string>
<string name="exception_httpexception">Der er opstået en HTTP-fejl.</string>
<string name="exception_ioexception">Der er opstået en I/O-fejl.</string>
<string name="exception_show_details">Vis detaljer</string>
<!--sync errors and DebugInfoActivity-->
<string name="debug_info_title">Debug-info</string>
<string name="sync_error_permissions">DAVdroid-rettigheder</string>
<string name="sync_error_permissions_text">Yderligere adgang påkrævet</string>
<string name="sync_error_calendar">Synkronisering af kalenderen lykkedes ikke (%s)</string>
<string name="sync_error_contacts">Synkronisering af adressebogen lykkedes ikke (%s)</string>
<string name="sync_error_tasks">Synkronisering af opgaver lykkedes ikke (%s)</string>
<string name="sync_error">Fejl under %s</string>
<string name="sync_error_http_dav">Serverfejl under %s</string>
<string name="sync_error_local_storage">Databasefejl under %s</string>
<string-array name="sync_error_phases">
<item>forbereder synkronisering</item>
<item>checker understøttelse</item>
<item>behandler poster, der er slettet lokalt</item>
<item>behandler poster, der er blevet oprettet/redigeret</item>
<item>uploader poster, der er blevet oprettet/redigeret</item>
<item>checker synkroniseringsstatus</item>
<item>laver liste af lokale poster</item>
<item>laver lister af poster på server</item>
<item>sammenligner poster lokalt og på server</item>
<item>downloader poster på server</item>
<item>efterbehandler</item>
<item>gemmer synkroniseringsstatus</item>
</string-array>
<string name="sync_error_unauthorized">Fejl i brugernavn/adgangskode</string>
<!--cert4android-->
<string name="certificate_notification_connection_security">DAVdroid: Forbindelsessikkerhed</string>
<string name="trust_certificate_unknown_certificate_found">DAVdroid er stødt på et ukendt certifikat. Vil du stole på det? </string>
</resources>
-241
View File
@@ -1,241 +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">Ayuda</string>
<string name="manage_accounts">Administrar cuentas</string>
<string name="please_wait">Por favor, espere...</string>
<string name="send">Enviar</string>
<!--startup dialogs-->
<string name="startup_battery_optimization">Optimización de batería</string>
<string name="startup_battery_optimization_message">Android puede desactivar/reducir la sincronización de DAVdroid después de unos días. Para prevenir esto, desactiva la optimización.</string>
<string name="startup_battery_optimization_disable">Apagar para DAVdroid</string>
<string name="startup_dont_show_again">No mostrar de nuevo</string>
<string name="startup_development_version_give_feedback">Dar retroalimentación</string>
<string name="startup_donate">Información de código abierto</string>
<string name="startup_donate_message">Nos alegra que uses DAVdroid, que es software de código abierto (GPLv3). Debido al duro trabajo que supone el desarrollo de DAVdroid y los cientos de horas de trabajo, por favor, considera hacer una donación.</string>
<string name="startup_donate_now">Mostrar página de donación</string>
<string name="startup_donate_later">Quizás luego</string>
<string name="startup_google_play_accounts_removed">Información de error de DRM de Play Store</string>
<string name="startup_google_play_accounts_removed_message">Bajo ciertas condiciones, el DRM de Play Store puede causar que todas las cuentas de DAVdroid se desconfiguren tras un reinicio o una actualización de DAVdroid. Si esto le afecta (y sólo en ese caso), por favor, instale \"DAVdroid JB Workaround\" desde Play Store.</string>
<string name="startup_google_play_accounts_removed_more_info">Más información</string>
<string name="startup_opentasks_not_installed">OpenTasks no está instalado</string>
<string name="startup_opentasks_not_installed_message">La aplicación OpenTasks no está disponible. DAVdroid no podrá sincronizar listas de tareas.</string>
<string name="startup_opentasks_reinstall_davdroid">Tras instalar OpenTasks, tendrás que re-instalar DAVdroid y añadir tus cuentas de nuevo (por un error de Android).</string>
<string name="startup_opentasks_not_installed_install">Instalar OpenTasks</string>
<!--AboutActivity-->
<string name="about_license_terms">Términos de la licencia</string>
<string name="about_license_info_no_warranty">Este programa viene sin NINGÚN TIPO DE GARANTÍA. Es software libre, y cualquier contribución es bienvenida y redistribuida bajo ciertas condiciones.</string>
<!--global settings-->
<string name="logging_davdroid_file_logging">Archivo de registro de DAVdroid</string>
<string name="logging_to_external_storage">Registrar en almacenamiento externo: %s</string>
<string name="logging_to_external_storage_warning">¡Eliminar todos los registros tan pronto como sea posible!</string>
<string name="logging_couldnt_create_file">No se puede crear el archivo de registro externo: %s</string>
<string name="logging_no_external_storage">Almacenamiento externo no encontrado</string>
<!--AccountsActivity-->
<string name="navigation_drawer_open">Abrir panel de navegación</string>
<string name="navigation_drawer_close">Cerrar panel de navegación</string>
<string name="navigation_drawer_subtitle">Adaptador de sincronización CalDAV/CardDAV</string>
<string name="navigation_drawer_about">Acerca de / Licencia</string>
<string name="navigation_drawer_settings">Ajustes</string>
<string name="navigation_drawer_news_updates">Noticias y actualizaciones</string>
<string name="navigation_drawer_external_links">Enlaces externos</string>
<string name="navigation_drawer_website">Sitio web</string>
<string name="navigation_drawer_faq">Preguntas frequentes</string>
<string name="navigation_drawer_donate">Donar</string>
<string name="account_list_empty">Bienvenido a DAVdroid!\n\nAhora puedes añadir una cuenta CalDAV/CardDAV.</string>
<!--DavService-->
<string name="dav_service_refresh_failed">Falló la detección del servicio</string>
<string name="dav_service_refresh_couldnt_refresh">No se pudo refrescar lista de colección</string>
<!--AppSettingsActivity-->
<string name="app_settings">Ajustes</string>
<string name="app_settings_user_interface">Interfaz de usuario</string>
<string name="app_settings_reset_hints">Restablecer advertencias</string>
<string name="app_settings_reset_hints_summary">Habilita las advertencias que han sido rechazadas con anterioridad</string>
<string name="app_settings_reset_hints_success">Todas las advertencias se mostrarán nuevamente</string>
<string name="app_settings_connection">Conexión</string>
<string name="app_settings_override_proxy">Anular ajustes del proxy</string>
<string name="app_settings_override_proxy_on">Usar ajustes personalizados del proxy</string>
<string name="app_settings_override_proxy_off">Usar ajustes del proxy predefinidos por el sistema</string>
<string name="app_settings_override_proxy_host">Nombre del host del proxy HTTP</string>
<string name="app_settings_override_proxy_port">Puerto del proxy HTTP</string>
<string name="app_settings_security">Seguridad</string>
<string name="app_settings_distrust_system_certs">Invalidar los certificados del sistema</string>
<string name="app_settings_distrust_system_certs_on">Los CA del sistema y los añadidos por el usuario no serán válidos</string>
<string name="app_settings_distrust_system_certs_off">Los CA del sistema y los añadidos por el usuario serán usados y de confianza (recomendado)</string>
<string name="app_settings_reset_certificates">Reiniciar certificados (in)validados</string>
<string name="app_settings_reset_certificates_summary">Reinicia la validez de todos los certificados particulares</string>
<string name="app_settings_reset_certificates_success">Todos los certificados particulares han sido limpiados</string>
<string name="app_settings_debug">Depuración</string>
<string name="app_settings_log_to_external_storage">Registrar en fichero externo</string>
<string name="app_settings_log_to_external_storage_on">Registro en almacenamiento externo (si está disponible)</string>
<string name="app_settings_log_to_external_storage_off">El archivo de registro externo está deshabilitado</string>
<string name="app_settings_show_debug_info">Mostrar la información de depuración</string>
<string name="app_settings_show_debug_info_details">Ver/compartir detalles de software y configuración</string>
<!--AccountActivity-->
<string name="account_synchronize_now">Sincronizar ahora</string>
<string name="account_synchronizing_now">Sincronizando...</string>
<string name="account_settings">Ajustes de cuenta</string>
<string name="account_rename">Renombrar cuenta</string>
<string name="account_rename_new_name">Información local no guardada puede ser desechada. Se requiere resincronizar después de renombrar. Nuevo nombre de cuenta:</string>
<string name="account_rename_rename">Renombrar</string>
<string name="account_delete">Eliminar cuenta</string>
<string name="account_delete_confirmation_title">¿Seguro que deseas eliminar la cuenta?</string>
<string name="account_delete_confirmation_text">Todas las copias locales de tus contactos, calendarios y tareas serán eliminadas.</string>
<string name="account_refresh_address_book_list">Refrescar contactos</string>
<string name="account_create_new_address_book">Crear nueva lista de contactos</string>
<string name="account_refresh_calendar_list">Refrescar calendario</string>
<string name="account_create_new_calendar">Crear nuevo calendario</string>
<!--PermissionsActivity-->
<string name="permissions_title">Permisos de DAVdroid</string>
<string name="permissions_calendar">Permisos de calendario</string>
<string name="permissions_calendar_details">Para sincronizar eventos CalDAV con tus calendarios locales, DAVdroid necesita acceder a los mismos.</string>
<string name="permissions_calendar_request">Solicitar permisos sobre calendario</string>
<string name="permissions_contacts">Permisos de contactos</string>
<string name="permissions_contacts_details">Para sincronizar libretas de contactos CadDAV con tus contactos locales, DAVdroid necesita acceder a los mismos.</string>
<string name="permissions_contacts_request">Solicitar permisos sobre contactos</string>
<string name="permissions_opentasks">Permisos de OpenTasks</string>
<string name="permissions_opentasks_details">Para sincronizar listas de tareas CalDAV con tus listas de tareas locales, DAVdroid necesita acceder a OpenTasks.</string>
<string name="permissions_opentasks_request">Solicitar permisos sobre OpenTasks</string>
<!--AddAccountActivity-->
<string name="login_title">Añadir cuenta</string>
<string name="login_type_email">Acceder con cuenta de correo</string>
<string name="login_email_address">Dirección de correo</string>
<string name="login_email_address_error">Se requiere una dirección de correo válida</string>
<string name="login_password">Contraseña</string>
<string name="login_password_required">Contraseña requerida</string>
<string name="login_type_url">Acceder con URL y nombre de usuario</string>
<string name="login_url_must_be_http_or_https">La URL debe comenzar con http(s)://</string>
<string name="login_url_host_name_required">Nombre de servidor requerido</string>
<string name="login_user_name">Nombre de usuario</string>
<string name="login_user_name_required">Nombre de usuario requerido</string>
<string name="login_base_url">URL base</string>
<string name="login_login">Registrar</string>
<string name="login_back">Volver</string>
<string name="login_create_account">Crear cuenta</string>
<string name="login_account_name">Nombre de cuenta</string>
<string name="login_account_name_info">Usa tu dirección de correo como nombre de cuenta puesto que Android usará el nombre de la cuenta como campo de \"organizador\" en los eventos que cree. No puedes tener dos cuentas con el mismo nombre.</string>
<string name="login_account_contact_group_method">Método de contacto de grupo:</string>
<string name="login_account_name_required">Nombre de cuenta requerido</string>
<string name="login_account_not_created">La cuenta no pudo ser creada</string>
<string name="login_configuration_detection">Detectar configuración</string>
<string name="login_querying_server">Por favor espera, consultando al servidor...</string>
<string name="login_no_caldav_carddav">No se pudo encontrar el servicio CalDAV o CardDAV.</string>
<string name="login_view_logs">Ver registros</string>
<!--AccountSettingsActivity-->
<string name="settings_title">Ajustes: %s</string>
<string name="settings_authentication">Autenticación</string>
<string name="settings_username">Nombre de usuario</string>
<string name="settings_enter_username">Introduce tu nombre de usuario:</string>
<string name="settings_password">Contraseña</string>
<string name="settings_password_summary">Actualiza la contraseña de acuerdo a tu servidor.</string>
<string name="settings_enter_password">Introduce tu contraseña:</string>
<string name="settings_sync">Sincronización</string>
<string name="settings_sync_interval_contacts">Intervalo de sincronización de contactos</string>
<string name="settings_sync_summary_manually">Solo manualmente</string>
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Cada %d minutos + inmediatamente con cambios locales</string>
<string name="settings_sync_interval_calendars">Intervalo de sincronización de calendarios</string>
<string name="settings_sync_interval_tasks">Intervalo de sincronizacion de Tasks</string>
<string-array name="settings_sync_interval_seconds">
<item>-1</item>
<item>300</item>
<item>600</item>
<item>900</item>
<item>3600</item>
<item>7200</item>
<item>14400</item>
<item>86400</item>
</string-array>
<string-array name="settings_sync_interval_names">
<item>Solo manualmente</item>
<item>Cada 5 minutos</item>
<item>Cada 10 minutos</item>
<item>Cada 15 minutos</item>
<item>Cada hora</item>
<item>Cada 2 horas</item>
<item>Cada 4 horas</item>
<item>Una vez al día</item>
</string-array>
<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_ssid">Restricción WiFi SSID</string>
<string name="settings_sync_wifi_only_ssid_on">Sólo se sincronizará sobre %s</string>
<string name="settings_sync_wifi_only_ssid_off">Todas las conexiones WiFi pueden ser usadas</string>
<string name="settings_sync_wifi_only_ssid_message">Introduce el nombre de una red WiFi (SSID) para restringir la sincronización a esta red, o deja el campo en blanco para usar todas las conexiones WiFi.</string>
<string name="settings_carddav">CardDAV</string>
<string name="settings_contact_group_method">Método de contacto de grupo</string>
<string-array name="settings_contact_group_method_values">
<item>GROUP_VCARDS</item>
<item>CATEGORIES</item>
</string-array>
<string-array name="settings_contact_group_method_entries">
<item>Los groups tienen VCards separadas</item>
<item>Los groups tienen una categoría por contacto</item>
</string-array>
<string name="settings_caldav">CalDAV</string>
<string name="settings_sync_time_range_past">Límite de tiempo de eventos pasados</string>
<string name="settings_sync_time_range_past_none">Todos los eventos serán sincronizados</string>
<plurals name="settings_sync_time_range_past_days">
<item quantity="one">Los eventos anteriores a un día serán ignorados</item>
<item quantity="other">Los eventos anteriores a %d días serán ignorados</item>
</plurals>
<string name="settings_sync_time_range_past_message">Los eventos anteriores a este número de días serán ignorados (puede ser 0). Deja en blanco el campo para sincronizar todos los eventos.</string>
<string name="settings_manage_calendar_colors">Colores de calendario</string>
<string name="settings_manage_calendar_colors_on">Los colores de los calendarios son administrados por DAVdroid</string>
<string name="settings_manage_calendar_colors_off">Los colores de los calendarios no son establecidos por DAVdroid</string>
<!--collection management-->
<string name="create_addressbook">Crear nueva lista de contactos</string>
<string name="create_addressbook_display_name_hint">Agendas</string>
<string name="create_calendar">Crear colección CalDAV</string>
<string name="create_calendar_display_name_hint">Mi calendario</string>
<string name="create_calendar_time_zone">Zona horaria:</string>
<string name="create_calendar_type">Tipo de colección:</string>
<string name="create_calendar_type_only_events">Calendario (sólo eventos)</string>
<string name="create_calendar_type_only_tasks">Lista de tareas (sólo tareas)</string>
<string name="create_calendar_type_events_and_tasks">Combinado (eventos y tareas)</string>
<string name="create_collection_color">Establecer un color</string>
<string name="create_collection_creating">Crear colección</string>
<string name="create_collection_display_name">Nombre mostrado (título):</string>
<string name="create_collection_display_name_required">Título requerido</string>
<string name="create_collection_description">Descripción (opcional):</string>
<string name="create_collection_home_set">Establecer localización:</string>
<string name="create_collection_create">Crear</string>
<string name="delete_collection">Eliminar colección</string>
<string name="delete_collection_confirm_title">¿Estás seguro/a?</string>
<string name="delete_collection_confirm_warning">Esta colección (%s) y toda su información será eliminada del servidor.</string>
<string name="delete_collection_deleting_collection">Eliminando colección</string>
<!--ExceptionInfoFragment-->
<string name="exception">Ocurrió un error.</string>
<string name="exception_httpexception">Ha ocurrido un error HTTP.</string>
<string name="exception_ioexception">Ha ocurrido un error I/O.</string>
<string name="exception_show_details">Mostrar detalles</string>
<!--sync errors and DebugInfoActivity-->
<string name="debug_info_title">Información de depuración</string>
<string name="sync_error_permissions">Permisos de DAVdroid</string>
<string name="sync_error_permissions_text">Permisos adicionales requeridos</string>
<string name="sync_error_calendar">La sincronización de calendario falló (%s)</string>
<string name="sync_error_contacts">La sincronización de agenda falló (%s)</string>
<string name="sync_error_tasks">La sincronización de tareas falló (%s)</string>
<string name="sync_error">Error al %s</string>
<string name="sync_error_http_dav">Error de servidor al %s</string>
<string name="sync_error_local_storage">Error de base de datos al %s</string>
<string-array name="sync_error_phases">
<item>preparando sincronización</item>
<item>buscando capacidades</item>
<item>procesando entradas borradas localmente</item>
<item>preparando entradas creadas/modificadas</item>
<item>cargando entradas creadas/modificadas</item>
<item>comprobando estado de sincronización</item>
<item>enumerando entradas locales</item>
<item>enumerando entradas remotas</item>
<item>comparando entradas locales/remotas</item>
<item>descargando entradas remotas</item>
<item>post-procesando</item>
<item>guardando estado de sincronización</item>
</string-array>
<string name="sync_error_unauthorized">Nombre de usuario/contraseña erróneo</string>
<!--cert4android-->
<string name="certificate_notification_connection_security">DAVdroid: Seguridad de conexión</string>
<string name="trust_certificate_unknown_certificate_found">DAVdroid ha encontrado un certificado desconocido. ¿Quieres que sea válido?</string>
</resources>
-246
View File
@@ -1,246 +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">SVP attendez ...</string>
<string name="send">Envoyer</string>
<!--startup dialogs-->
<string name="startup_battery_optimization">Optimisation de la batterie</string>
<string name="startup_battery_optimization_message">Android peut désactiver/réduire la synchronisation de DAVdroid après quelques jours. Pour éviter cela, désactivez l\'optimisation de la batterie.</string>
<string name="startup_battery_optimization_disable">Désactiver pour DAVdroid</string>
<string name="startup_dont_show_again">Ne plus afficher</string>
<string name="startup_development_version_give_feedback">Faire un commentaire</string>
<string name="startup_donate">Open-Source Information</string>
<string name="startup_donate_message">Nous sommes heureux que vous utilisez DAVdroid, qui est un logiciel open-source (GPLv3). Parce que développer DAVdroid est un travail difficile et nous a pris de nombreuses heures, s\'il vous plaît envisager de faire un don.</string>
<string name="startup_donate_now">Faire un don</string>
<string name="startup_donate_later">Plus tard</string>
<string name="startup_google_play_accounts_removed">Erreur information Play Store DRM</string>
<string name="startup_google_play_accounts_removed_message">Dans certaines conditions, Play Store DRM peut provoquer la disparition de tous les comptes DAVdroid après un redémarrage ou après la mise à niveau de DAVdroid. Si vous êtes concerné par ce problème (et seulement alors), s\'il vous plaît installer \"DAVdroid JB Solution\" du Play Store.</string>
<string name="startup_google_play_accounts_removed_more_info">Plus d\'informations</string>
<string name="startup_opentasks_not_installed">OpenTasks n\'est pas installé</string>
<string name="startup_opentasks_not_installed_message">L\'application OpenTasks n\'est pas disponible, donc DAVdroid ne pourra pas synchroniser des listes de tâches.</string>
<string name="startup_opentasks_reinstall_davdroid">Après l\'installation OpenTasks, vous devez RE-INSTALLER DAVdroid et ajoutez vos comptes à nouveau (bug Android).</string>
<string name="startup_opentasks_not_installed_install">Installer OpenTasks</string>
<!--AboutActivity-->
<string name="about_license_terms">Conditions d\'utilisation</string>
<string name="about_license_info_no_warranty">Ce programme est fourni sans AUCUNE GARANTIE. C\'est un logiciel libre, et vous êtes en droit de le redistribuer sous certaines conditions.</string>
<!--global settings-->
<string name="logging_davdroid_file_logging">DAVdroid fichier de journalisation</string>
<string name="logging_to_external_storage">Se connecter au stockage externe: %s</string>
<string name="logging_to_external_storage_warning">Supprimer les journaux dès que possible!</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é &amp; mise à jour</string>
<string name="navigation_drawer_external_links">Liens externes</string>
<string name="navigation_drawer_website">Site Web</string>
<string name="navigation_drawer_faq">FAQ</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/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_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>
<!--PermissionsActivity-->
<string name="permissions_title">Autorisations DAVdroid</string>
<string name="permissions_calendar">Autorisations calendrier</string>
<string name="permissions_calendar_details">Pour synchroniser les événements CalDAV avec vos calendriers locaux, DAVdroid a besoin d\'accéder à vos calendriers.</string>
<string name="permissions_calendar_request">Demande d\'autorisations d\'accéder au calendrier</string>
<string name="permissions_contacts">Autorisations contacts</string>
<string name="permissions_contacts_details">Pour synchroniser les carnets d\'adresses de CardDAV avec votre carnet d\'adresses local, DAVdroid a besoin d\'accéder à vos contacts.</string>
<string name="permissions_contacts_request">Demande d\'autorisations d\'accéder aux contacts</string>
<string name="permissions_opentasks">Autorisations OpenTasks</string>
<string name="permissions_opentasks_details">Pour synchroniser les tâches de CalDAV avec vos listes de tâches locales, DAVdroid a besoin d\'accéder à OpenTasks.</string>
<string name="permissions_opentasks_request">Demande d\'autorisations d\'accéder à OpenTasks</string>
<!--AddAccountActivity-->
<string name="login_help_url">https://davdroid.bitfire.at/configuration/?pk_campaign=davdroid-app</string>
<string name="login_title">Ajouter un compte</string>
<string name="login_type_email">Connexion avec une adresse email</string>
<string name="login_email_address">Adresse mail</string>
<string name="login_email_address_error">Une adresse e-mail valide est requise</string>
<string name="login_password">Mot de passe</string>
<string name="login_password_required">Mot de passe requis</string>
<string name="login_type_url">Connexion avec une URL et un nom d\'utilisateur</string>
<string name="login_url_must_be_http_or_https">L\'URL doit commencer par http(s)://</string>
<string name="login_url_host_name_required">Nom d\'hôte requis</string>
<string name="login_user_name">Nom d\'utilisateur</string>
<string name="login_user_name_required">Nom d\'utilisateur requis</string>
<string name="login_base_url">URL de base</string>
<string name="login_login">Se connecter</string>
<string name="login_back">Retour</string>
<string name="login_create_account">Créer un compte</string>
<string name="login_account_name">Nom du compte</string>
<string name="login_account_name_info">Utilisez votre adresse e-mail comme nom de compte car Android utilisera ce nom en tant que champ ORGANISATEUR pour les événements que vous créerez. Vous ne pouvez pas avoir deux comptes avec le même nom.</string>
<string name="login_account_contact_group_method">Méthode pour les contacts de type groupe :</string>
<string name="login_account_name_required">Nom du compte requis</string>
<string name="login_account_not_created">Le compte n\'a pas pu être créé</string>
<string name="login_configuration_detection">Détection de la configuration</string>
<string name="login_querying_server">S\'il vous plaît patienter, nous interrogeons le serveur ...</string>
<string name="login_no_caldav_carddav">Aucun service CalDAV ou CardDAV trouvé.</string>
<string name="login_view_logs">Voir infos de débogage</string>
<!--AccountSettingsActivity-->
<string name="settings_title">Paramètres: %s</string>
<string name="settings_authentication">Authentification</string>
<string name="settings_username">Nom d\'utilisateur</string>
<string name="settings_enter_username">Saisissez votre nom d\'utilisateur :</string>
<string name="settings_password">Mot de passe</string>
<string name="settings_password_summary">Mettre à jour le mot de passe </string>
<string name="settings_enter_password">Saisissez votre mot de passe :</string>
<string name="settings_sync">Synchronisation</string>
<string name="settings_sync_interval_contacts">Intervalle de synchronisation des carnets d\'adresses</string>
<string name="settings_sync_summary_manually">Manuellement</string>
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Toutes les %d minutes et immédiatement après un changement local</string>
<string name="settings_sync_interval_calendars">Intervalle de synchronisation des agendas</string>
<string name="settings_sync_interval_tasks">Intervalle de synchronisation des tâches</string>
<string-array name="settings_sync_interval_seconds">
<item>-1</item>
<item>300</item>
<item>600</item>
<item>900</item>
<item>3600</item>
<item>7200</item>
<item>14400</item>
<item>86400</item>
</string-array>
<string-array name="settings_sync_interval_names">
<item>Manuellement</item>
<item>Toutes les 5 minutes</item>
<item>Toutes les 10 minutes</item>
<item>Toutes les 15 minutes</item>
<item>Toutes les heures</item>
<item>Toutes les 2 heures</item>
<item>Toutes les 4 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_ssid">Restriction WiFi SSID</string>
<string name="settings_sync_wifi_only_ssid_on">Sera seulement synchroniser avec %s</string>
<string name="settings_sync_wifi_only_ssid_off">Toutes les connexions WiFi peuvent être utilisées</string>
<string name="settings_sync_wifi_only_ssid_message">Entrez le nom d\'un réseau WiFi (SSID) pour restreindre la synchronisation à ce réseau, ou laisser vide pour autoriser toutes les connexions WiFi.</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>
<!--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 errors and DebugInfoActivity-->
<string name="debug_info_title">Infos de débogage</string>
<string name="sync_error_permissions">Autorisations DAVdroid</string>
<string name="sync_error_permissions_text">Autorisations supplémentaires demandées</string>
<string name="sync_error_calendar">Échec de la synchronisation du calendrier (%s)</string>
<string name="sync_error_contacts">Échec de la synchronisation du carnet d\'adresse (%s)</string>
<string name="sync_error_tasks">Échec de la synchronisation (%s)</string>
<string name="sync_error">Erreur durant %s</string>
<string name="sync_error_http_dav">Erreur de serveur durant %s</string>
<string name="sync_error_local_storage">Erreur de base de donnée durant %s</string>
<string-array name="sync_error_phases">
<item>prépare la synchronisation</item>
<item>demande les autorisations</item>
<item>procède à la suppression des entrées locales</item>
<item>prépare les entrées créées/modifiées</item>
<item>envoi les entrées créées/modifiées</item>
<item>vérifie l\'état de la synchronisation</item>
<item>liste les entrées locales</item>
<item>liste les entrées distantes</item>
<item>compare les entrées locales/distantes</item>
<item>télécharge les entrées distantes</item>
<item>post-traitement</item>
<item>enregistre l\'état de la synchronisation</item>
</string-array>
<string name="sync_error_unauthorized">Nom d\'utilisateur ou mot de passe erroné</string>
<!--cert4android-->
<string name="certificate_notification_connection_security">DAVdroid : Sécurité de la connexion</string>
<string name="trust_certificate_unknown_certificate_found">DAVdroid a rencontré un certificat inconnu. Voulez-vous lui faire confiance?</string>
</resources>
-245
View File
@@ -1,245 +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 címjegyzék</string>
<string name="address_books_authority_title">Címjegyzékek</string>
<string name="help">Súgó</string>
<string name="manage_accounts">Fiókok kezelése</string>
<string name="please_wait">Kérjük, várjon ...</string>
<string name="send">Küldés</string>
<!--startup dialogs-->
<string name="startup_battery_optimization">Akkumulátoroptimalizálás </string>
<string name="startup_battery_optimization_message">Az operációs rendszer a DAVdroid szinkronizálást pár nap után leállíthatja vagy visszafoghatja. Ennek elkerülésére kapcsolja ki az akkumulátoroptimalizálást.</string>
<string name="startup_battery_optimization_disable">Kikapcsolás a DAVdroid kapcsán</string>
<string name="startup_dont_show_again">Ne jelenjen meg többet</string>
<string name="startup_development_version_give_feedback">Visszajelzés küldése</string>
<string name="startup_donate">A forrás nyíltságával kapcsolatos információk</string>
<string name="startup_donate_message">Örülünk, hogy használja a DAVdroidot. A DAVdroid nyílt forráskódú (GPLv3) szoftver, de a fejlesztése kemény munkát jelent, már eddig több ezer munkaórát emésztett fel, ezért kérjük, fontolja meg, hogy támogassa munkánkat.</string>
<string name="startup_donate_now">Támogatás küldése</string>
<string name="startup_donate_later">Talán később</string>
<string name="startup_google_play_accounts_removed">Play Áruház DRM hibával kapcsolatos információ</string>
<string name="startup_google_play_accounts_removed_message">Bizonyos körülmények között a Play Áruház DRM okozhatja azt, hogy az eszköz újraindítását vagy a DAVdroid frissítését követően a DAVdroid fiókok eltűnnek. Amennyiben (és csak amennyiben) érinti Önt ez a probléma, telepítse a \"DAVdroid JB Workaround\" alkalmazást Play Áruházból.</string>
<string name="startup_google_play_accounts_removed_more_info">További információk</string>
<string name="startup_opentasks_not_installed">Az OpenTasks nincs telepítve</string>
<string name="startup_opentasks_not_installed_message">Az OpenTasks alkalmazás nincs telepítve, így a DAVdroid nem lesz képes szinkronizálni feladatlistákat.</string>
<string name="startup_opentasks_reinstall_davdroid">Az OpenTasks telepítését követően újra kell telepíteni a DAVdroit alkalmazást és újra fel kell venni a fiókokat (Android hiba).</string>
<string name="startup_opentasks_not_installed_install">Az OpenTasks telepítése</string>
<!--AboutActivity-->
<string name="about_license_terms">Licencfeltételek</string>
<string name="about_license_info_no_warranty">Ehhez a program SEMMIFÉLE GARANCIA NEM JÁR. Ez a program szabad szoftver, ami a bizonyos feltételek mellett szabadon terjeszthető.</string>
<!--global settings-->
<string name="logging_davdroid_file_logging">DAVdroid fájlalapú naplózás</string>
<string name="logging_to_external_storage">Naplózás külső tárhelyre: %s</string>
<string name="logging_to_external_storage_warning">Törölje a naplófájlt, amint lehetséges!</string>
<string name="logging_couldnt_create_file">Külső naplófájl létrehozása sikertelen: %s</string>
<string name="logging_no_external_storage">A külső tárhely nem érhető el</string>
<!--AccountsActivity-->
<string name="navigation_drawer_open">Navigációs fiók megnyitása</string>
<string name="navigation_drawer_close">Navigációs fiók lezárása</string>
<string name="navigation_drawer_subtitle">CalDAV/CardDAV szinkronizációs adapter</string>
<string name="navigation_drawer_about">Névjegy / Licenc</string>
<string name="navigation_drawer_settings">Beállítások</string>
<string name="navigation_drawer_news_updates">Hírek és frissítések</string>
<string name="navigation_drawer_external_links">Weblapok</string>
<string name="navigation_drawer_website">Honlap</string>
<string name="navigation_drawer_faq">GYIK</string>
<string name="navigation_drawer_donate">Támogatás</string>
<string name="account_list_empty">Üdvözöljük a DAVdroid felhasználók között!\n\nMost már felvehet CalDAV/CardDav fiókokat.</string>
<string name="accounts_global_sync_disabled">A rendszerszintű automatikus szinkronizálás ki van kapcsolva</string>
<string name="accounts_global_sync_enable">Bekapcsolás</string>
<!--DavService-->
<string name="dav_service_refresh_failed">Szolgáltatások felderítése nem sikerült</string>
<string name="dav_service_refresh_couldnt_refresh">Gyűjteménylista frissítése nem sikerült</string>
<!--AppSettingsActivity-->
<string name="app_settings">Beállítások</string>
<string name="app_settings_user_interface">Felhasználói felület</string>
<string name="app_settings_reset_hints">Tippek visszaállítása</string>
<string name="app_settings_reset_hints_summary">Újra jelenjen meg az összes tipp</string>
<string name="app_settings_reset_hints_success">Az összes tipp újra meg fog jelenni</string>
<string name="app_settings_connection">Kapcsolat</string>
<string name="app_settings_override_proxy">Proxybeállítások felülírása</string>
<string name="app_settings_override_proxy_on">Egyedi proxybeállítások</string>
<string name="app_settings_override_proxy_off">Az alapértelmezett proxybeállítás használata</string>
<string name="app_settings_override_proxy_host">HTTP proxyállomás neve</string>
<string name="app_settings_override_proxy_port">HTTP proxy port</string>
<string name="app_settings_security">Biztonság</string>
<string name="app_settings_distrust_system_certs">A rendszertanúsítványok elfogadása</string>
<string name="app_settings_distrust_system_certs_on">A rendszer által kezelt, előre vagy felhasználó által telepített tanúsítványok figyelmen kívül lesznek hagyva</string>
<string name="app_settings_distrust_system_certs_off">A rendszer által kezelt, előre vagy felhasználó által telepített tanúsítványok megbízhatóak (javasolt)</string>
<string name="app_settings_reset_certificates">A tanúsítványok megbízhatóságának törlésére</string>
<string name="app_settings_reset_certificates_summary">A tanúsítványok megbízhatóságával kapcsolatos beállítások törlésére</string>
<string name="app_settings_reset_certificates_success">A tanúsítványok megbízhatóságával kapcsolatos beállítások törölve</string>
<string name="app_settings_debug">Hibakeresés</string>
<string name="app_settings_log_to_external_storage">Naplózás fájlba</string>
<string name="app_settings_log_to_external_storage_on">Naplózás külső tárhelyre (ha elérhető)</string>
<string name="app_settings_log_to_external_storage_off">Fájlalapú naplózás kikapcsolva</string>
<string name="app_settings_show_debug_info">Hibakeresési információ megtekintése</string>
<string name="app_settings_show_debug_info_details">Szoftver- és konfigurációs részletek megtekintése/megosztása</string>
<!--AccountActivity-->
<string name="account_synchronize_now">Szinkronizálás most</string>
<string name="account_synchronizing_now">Szinkronizálás</string>
<string name="account_settings">Fiókbeállítások</string>
<string name="account_rename">Fiók átnevezése</string>
<string name="account_rename_new_name">Az elmentetlen helyben tárolt adatok elvesznek. Az átnevezés után szinkronizálásra lesz szükség. Új fióknév:</string>
<string name="account_rename_rename">Átnevez</string>
<string name="account_delete">Fiók törlése</string>
<string name="account_delete_confirmation_title">Valóban törölni akarja a fiókot?</string>
<string name="account_delete_confirmation_text">Az összes címjegyzék, naptár és feladatlista helyi példányai törölve lesznek.</string>
<string name="account_refresh_address_book_list">Címjegyzék-lista frissítése</string>
<string name="account_create_new_address_book">Új címjegyzék létrehozása</string>
<string name="account_refresh_calendar_list">Naptárlista frissítése</string>
<string name="account_create_new_calendar">Új naptár létrehozása</string>
<!--PermissionsActivity-->
<string name="permissions_title">DAVdroid engedélyek </string>
<string name="permissions_calendar">Naptárengedély</string>
<string name="permissions_calendar_details">A CalDAV naptárak és a helyi naptárak szinkronizálásához a DAVdroid naptárhozzáférést igényel.</string>
<string name="permissions_calendar_request">Naptárhozzáférés igénylése</string>
<string name="permissions_contacts">Névjegyengedélyek</string>
<string name="permissions_contacts_details">A CardDAV címlisták és a helyi címlisták szinkronizálásához a névjegyhozzáférést igényel.</string>
<string name="permissions_contacts_request">Névjegyengedélyek igénylése</string>
<string name="permissions_opentasks">OpenTasks engedélyek</string>
<string name="permissions_opentasks_details">A CalDAV feladatlisták és a helyi feladatlisták szinkronizálásához a DAVdroid OpenTasks hozzáférést igényel.</string>
<string name="permissions_opentasks_request">OpenTasks engedélyek igénylése</string>
<!--AddAccountActivity-->
<string name="login_title">Fiók hozzáadása</string>
<string name="login_type_email">Bejelentkezés email cím segítségével</string>
<string name="login_email_address">Email cím:</string>
<string name="login_email_address_error">Érvényes email cím megadása feltétlenül szükséges</string>
<string name="login_password">Jelszó</string>
<string name="login_password_required">A jelszó megadása szükséges</string>
<string name="login_type_url">Bejelentkezés URL és felhasználónév segítségével</string>
<string name="login_url_must_be_http_or_https">Az URL elején szerepeljen http(s)://</string>
<string name="login_url_host_name_required">A szervernév megadása feltétlenül szükséges</string>
<string name="login_user_name">Felhasználónév</string>
<string name="login_user_name_required">A felhasználónév megadása feltétlenül szükséges</string>
<string name="login_base_url">URL-törzs</string>
<string name="login_login">Bejelentkezés</string>
<string name="login_back">Vissza</string>
<string name="login_create_account">Fiók létrehozása</string>
<string name="login_account_name">A fiók neve</string>
<string name="login_account_name_info">Használja az email címet fióknévként, mert később a létrehozandó események szervezőjeként (ORGANIZER mező) az Android ezt fogja használni. Két fiókot nem lehet azonos néven létrehozni.</string>
<string name="login_account_contact_group_method">A csoportok kezelésének módja:</string>
<string name="login_account_name_required">A fióknév megadása feltétlenül szükséges</string>
<string name="login_account_not_created">A fiók létrehozása nem sikerült</string>
<string name="login_configuration_detection">A konfiguráció felderítése</string>
<string name="login_querying_server">Kérjük, várjon, a szerver lekérdezése...</string>
<string name="login_no_caldav_carddav">Nem található CalDAV vagy CardDAV szolgáltatás.</string>
<string name="login_view_logs">Naplóbejegyzések megtekintése</string>
<!--AccountSettingsActivity-->
<string name="settings_title">Beállítások: %s</string>
<string name="settings_authentication">Authentikáció</string>
<string name="settings_username">Felhasználónév</string>
<string name="settings_enter_username">Adja meg a felhasználónevet:</string>
<string name="settings_password">Jelszó</string>
<string name="settings_password_summary">Adja meg a szerveren érvényes új jelszót.</string>
<string name="settings_enter_password">Adja meg a jelszót:</string>
<string name="settings_sync">Szinkronizálás</string>
<string name="settings_sync_interval_contacts">Névjegyszinkronizálás sűrűsége</string>
<string name="settings_sync_summary_manually">Manuális</string>
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Minden %d percben + az eszközön történt módosítás után</string>
<string name="settings_sync_interval_calendars">Naptárszinkronizálás sűrűsége</string>
<string name="settings_sync_interval_tasks">Feladatlisták szinkronizálásának sűrűsége</string>
<string-array name="settings_sync_interval_seconds">
<item>-1</item>
<item>300</item>
<item>600</item>
<item>900</item>
<item>3600</item>
<item>7200</item>
<item>14400</item>
<item>86400</item>
</string-array>
<string-array name="settings_sync_interval_names">
<item>Manuális</item>
<item>5 percenként</item>
<item>10 percenként</item>
<item>15 percenként</item>
<item>Óránként</item>
<item>2 óránként</item>
<item>4 óránként</item>
<item>Naponta</item>
</string-array>
<string name="settings_sync_wifi_only">Szinkronizálás csak WIFI-n</string>
<string name="settings_sync_wifi_only_on">Csak WIFI kapcsolat keresztül történjen szinkronizálás</string>
<string name="settings_sync_wifi_only_off">Szinkronizálás a kapcsolat típusától függetlenül</string>
<string name="settings_sync_wifi_only_ssid">WIFI SSID szűkítés</string>
<string name="settings_sync_wifi_only_ssid_on">Szinkronizálás csak a(z) %s hálózatra kapcsolódva</string>
<string name="settings_sync_wifi_only_ssid_off">Szinkronizálás bármely WIFI hálózaton</string>
<string name="settings_sync_wifi_only_ssid_message">Adja meg a WIFI hálózat nevét (SSID) a szinkronizálás egy hálózatra való korlátozához, vagy hagyja üresen, ha nem akar ilyen szűkítést.</string>
<string name="settings_carddav">CardDAV</string>
<string name="settings_contact_group_method">A csoportok kezelésének módja</string>
<string-array name="settings_contact_group_method_values">
<item>GROUP_VCARDS</item>
<item>CATEGORIES</item>
</string-array>
<string-array name="settings_contact_group_method_entries">
<item>Minden csoport egy különálló VCard</item>
<item>A csoportok a kapcsolatonkéni kategóriák</item>
</string-array>
<string name="settings_caldav">CalDAV</string>
<string name="settings_sync_time_range_past">Múltbéli események időkorlátja</string>
<string name="settings_sync_time_range_past_none">Minden esemény szinkronizálása</string>
<plurals name="settings_sync_time_range_past_days">
<item quantity="one">Az egy napnál régebbi események figyelmen kívül hagyása</item>
<item quantity="other">A %d napnál régebbi események figyelmen kívül hagyása</item>
</plurals>
<string name="settings_sync_time_range_past_message">Az ennyi napnál (lehet 0) régebbi események figyelmen kívül lesznek hagyva. Hagyja üresen, ha minden múltbéli eseményt szinkronizálni akar.</string>
<string name="settings_manage_calendar_colors">Naptárszínek kezelése</string>
<string name="settings_manage_calendar_colors_on">A naptárszíneket a DAVdroid kezeli</string>
<string name="settings_manage_calendar_colors_off">A naptárszíneket nem a DAVdroid kezeli</string>
<!--collection management-->
<string name="create_addressbook">Címjegyzék létrehozása</string>
<string name="create_addressbook_display_name_hint">Új címjegyzék</string>
<string name="create_calendar">CalDAV gyűjtemény létrehozása</string>
<string name="create_calendar_display_name_hint">Új naptár</string>
<string name="create_calendar_time_zone">Időzóna:</string>
<string name="create_calendar_type">Gyűjtemény típusa:</string>
<string name="create_calendar_type_only_events">Naptár (csak események)</string>
<string name="create_calendar_type_only_tasks">Feladatlista (csak feladatok)</string>
<string name="create_calendar_type_events_and_tasks">Kombinált (események és feladatok)</string>
<string name="create_collection_color">A gyűjtemény színének kiválasztása</string>
<string name="create_collection_creating">Gyűjtemény létrehozása</string>
<string name="create_collection_display_name">A gyűjtemény megnevezése (címe)</string>
<string name="create_collection_display_name_required">A cím megadása feltétlenül szükséges</string>
<string name="create_collection_description">Leírás (opcionális):</string>
<string name="create_collection_home_set">Gyűjtemények tárolója:</string>
<string name="create_collection_create">Létrehozás</string>
<string name="delete_collection">Gyűjtemény törlése</string>
<string name="delete_collection_confirm_title">Biztos?</string>
<string name="delete_collection_confirm_warning">A gyűjtemény (%s) és a hozzá tartozó adatok törölve lesznek a szerverről.</string>
<string name="delete_collection_deleting_collection">Gyűjtemény törlése</string>
<!--ExceptionInfoFragment-->
<string name="exception">Hiba történt.</string>
<string name="exception_httpexception">HTTP hiba történt.</string>
<string name="exception_ioexception">I/O hiba történt.</string>
<string name="exception_show_details">Részletek megjelenítése</string>
<!--sync errors and DebugInfoActivity-->
<string name="debug_info_title">Hibakeresési információ</string>
<string name="sync_error_permissions">DAVdroid engedélyek </string>
<string name="sync_error_permissions_text">További engedélyek szükségesek</string>
<string name="sync_error_calendar">A naptár szinkronizálása nem sikerült (%s)</string>
<string name="sync_error_contacts">A címjegyzék szinkronizálása nem sikerült (%s)</string>
<string name="sync_error_tasks">A feladatok szinkronizálása nem sikerült (%s)</string>
<string name="sync_error">Hiba az alábbi művelet közben: %s</string>
<string name="sync_error_http_dav">Szerver oldali hiba az alábbi művelet közben: %s</string>
<string name="sync_error_local_storage">Adatbázishiba az alábbi művelet közben: %s</string>
<string-array name="sync_error_phases">
<item>szinkronizáció előkészítése </item>
<item>szerver képességeinek lekérdezése</item>
<item>a helyben törölt bejegyzések feldolgozása</item>
<item>az új vagy módosított bejegyzések gyűjtése </item>
<item>az új vagy módosított bejegyzések feltöltése</item>
<item>szinkronizációs állapot ellenőrzése</item>
<item>helyi bejegyzések listázása</item>
<item>távoli bejegyzések listázása</item>
<item>helyi és távoli bejegyzések összehasonlítása</item>
<item>távoli bejegyzések letöltése</item>
<item>utófeldolgozás</item>
<item>szinkronizációs állapot mentése</item>
</string-array>
<string name="sync_error_unauthorized">A felhasználónév vagy jelszó hibás</string>
<!--cert4android-->
<string name="certificate_notification_connection_security">DAVdroid: kapcsolatbiztonság</string>
<string name="trust_certificate_unknown_certificate_found">Egy eddig ismeretlen tanúsítvány érkezett. Megbízhatónak kívánja elfogadni?</string>
</resources>
-245
View File
@@ -1,245 +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">Rubrica degli indirizzi DAVdroid</string>
<string name="address_books_authority_title">Rubriche degli indirizzi</string>
<string name="help">Aiuto</string>
<string name="manage_accounts">Gestione account</string>
<string name="please_wait">Attendere prego …</string>
<string name="send">Invia</string>
<!--startup dialogs-->
<string name="startup_battery_optimization">Ottimizazione della batteria</string>
<string name="startup_battery_optimization_message">Android può ridurre o disabilitare la sincronizzazione di DAVdroid dopo alcuni giorni. Per prevenire questo comportamento disabilita l\'ottimizzazione della batteria</string>
<string name="startup_battery_optimization_disable">Disabilita per DAVdroid</string>
<string name="startup_dont_show_again">Non mostrare più</string>
<string name="startup_development_version_give_feedback">Invia un rapporto</string>
<string name="startup_donate">Informazioni sull\'Open-Source</string>
<string name="startup_donate_message">Siamo soddisfatti del tuo uso di DAVdroid, programma Open-Source (GPLv3). Poiché lo sviluppo è un\'iniziativa complessa che comporta molte ore di lavoro ti invitiamo a fare una donazione.</string>
<string name="startup_donate_now">Mostra pagina donazioni</string>
<string name="startup_donate_later">Più tardi</string>
<string name="startup_google_play_accounts_removed">Informazioni sul bug del DRM di Play Store</string>
<string name="startup_google_play_accounts_removed_message">In alcune condizioni il DRM di Play Store può causare la perdita di tutti gli account DAVdroid dopo un riavvio o dopo un aggiornamento di DAVdroid. Se verificate questo problema installate successivamente \"DAVdroid JB Workaround\" da Play Store.</string>
<string name="startup_google_play_accounts_removed_more_info">Maggiori informazioni</string>
<string name="startup_opentasks_not_installed">OpenTasks non installata</string>
<string name="startup_opentasks_not_installed_message">L\'applicazione OpenTasks non è installata: di conseguenza DAVdroid non potrà sincronizzare l\'elenco delle attività.</string>
<string name="startup_opentasks_reinstall_davdroid">Dopo l\'installazione di OpenTasks è necessario INSTALLARE NUOVAMENTE DAVdroid e aggiungere ancora gli account (per un bug di Android).</string>
<string name="startup_opentasks_not_installed_install">Installa OpenTasks</string>
<!--AboutActivity-->
<string name="about_license_terms">Termini di licenza</string>
<string name="about_license_info_no_warranty">Il programma è distribuito SENZA ALCUNA GARANZIA. È software libero e può essere redistribuito sotto alcune condizioni.</string>
<!--global settings-->
<string name="logging_davdroid_file_logging">Invio del log di DAVdroid su file</string>
<string name="logging_to_external_storage">Log su dispositivo esterno: %s</string>
<string name="logging_to_external_storage_warning">Cancellare prima possibile i file di log!</string>
<string name="logging_couldnt_create_file">Non riesco a creare il file di log esterno: %s</string>
<string name="logging_no_external_storage">Dispositivo esterno non disponibile</string>
<!--AccountsActivity-->
<string name="navigation_drawer_open">Apri barra di navigazione</string>
<string name="navigation_drawer_close">Chiudi barra di navigazione</string>
<string name="navigation_drawer_subtitle">CalDAV/CardDAV adattatore di sincronizzazione</string>
<string name="navigation_drawer_about">Informazioni / Licenza</string>
<string name="navigation_drawer_settings">Impostazioni</string>
<string name="navigation_drawer_news_updates">Notizie &amp; aggiornamenti</string>
<string name="navigation_drawer_external_links">Link esterni</string>
<string name="navigation_drawer_website">Sito web</string>
<string name="navigation_drawer_faq">Domande Frequenti</string>
<string name="navigation_drawer_donate">Donazione</string>
<string name="account_list_empty">Benvenuto a DAVdroid!\n\nÈ ora possibile aggiungere account CalDAV/CardDAV.</string>
<string name="accounts_global_sync_disabled">La sincronizzazione automatica dell\'intero sistema è disabilitata</string>
<string name="accounts_global_sync_enable">Attiva</string>
<!--DavService-->
<string name="dav_service_refresh_failed">Fallita l\'individuazione dei servizi</string>
<string name="dav_service_refresh_couldnt_refresh">Impossibile aggiornare la lista delle raccolte</string>
<!--AppSettingsActivity-->
<string name="app_settings">Impostazioni</string>
<string name="app_settings_user_interface">Interfaccia utente</string>
<string name="app_settings_reset_hints">Reimposta i suggerimenti</string>
<string name="app_settings_reset_hints_summary">Riabilita i suggerimenti precedentemente disabilitati</string>
<string name="app_settings_reset_hints_success">I suggerimenti verranno mostrati</string>
<string name="app_settings_connection">Connessione</string>
<string name="app_settings_override_proxy">Non rispettare la impostazioni del proxy</string>
<string name="app_settings_override_proxy_on">Impostazioni personalizzate del proxy</string>
<string name="app_settings_override_proxy_off">Usa le impostazioni di sistema del proxy</string>
<string name="app_settings_override_proxy_host">Nome host del proxy HTTP</string>
<string name="app_settings_override_proxy_port">Porta del proxy HTTP</string>
<string name="app_settings_security">Sicurezza</string>
<string name="app_settings_distrust_system_certs">Non ti fidare dei certificati di sistema</string>
<string name="app_settings_distrust_system_certs_on">Le CA di sistema e quelle aggiunte dall\'utente non sono affidabili</string>
<string name="app_settings_distrust_system_certs_off">Le CA di sistema e quelle aggiunte dall\'utente sono affidabili (raccomandato)</string>
<string name="app_settings_reset_certificates">Reimposta la fiducia in tutti i certificati</string>
<string name="app_settings_reset_certificates_summary">Reimposta la fiducia nei certificati aggiunti</string>
<string name="app_settings_reset_certificates_success">Sono stati cancellati tutti i certificati aggiunti</string>
<string name="app_settings_debug">Debug</string>
<string name="app_settings_log_to_external_storage">Log su file esterno</string>
<string name="app_settings_log_to_external_storage_on">Log su dispositivo esterno (se disponibile)</string>
<string name="app_settings_log_to_external_storage_off">Log su file esterno disabilitato</string>
<string name="app_settings_show_debug_info">Mostra informazioni di debug</string>
<string name="app_settings_show_debug_info_details">Mostra e condividi i dettagli del programma e della configurazione</string>
<!--AccountActivity-->
<string name="account_synchronize_now">Sincronizza adesso</string>
<string name="account_synchronizing_now">Sincronizzazione in corso</string>
<string name="account_settings">Impostazioni account</string>
<string name="account_rename">Rinomina account</string>
<string name="account_rename_new_name">I dati locali non salvati potrebbero essere rimossi. È necessario effettuare la risincronizzazione dopo la rinomina. Nuovo nome dell\'account:</string>
<string name="account_rename_rename">Rinomina</string>
<string name="account_delete">Elimina account</string>
<string name="account_delete_confirmation_title">Cancellare l\'account?</string>
<string name="account_delete_confirmation_text">Tutte le copie locali delle rubriche, dei calendari e degli elenchi attività verranno eliminate.</string>
<string name="account_refresh_address_book_list">Aggiorna elenco degli indirizzari</string>
<string name="account_create_new_address_book">Crea un nuovo indirizzario</string>
<string name="account_refresh_calendar_list">Aggiorna lista calendari</string>
<string name="account_create_new_calendar">Crea nuovo calendario</string>
<!--PermissionsActivity-->
<string name="permissions_title">Permessi DAVdroid</string>
<string name="permissions_calendar">Permessi calendario</string>
<string name="permissions_calendar_details">Per sincronizzare gli eventi CalDAV con i calendari locali DAVdroid deve avere l\'accesso ai tuoi calendari.</string>
<string name="permissions_calendar_request">Richiesta autorizzazione al calendario</string>
<string name="permissions_contacts">Permessi Contatti</string>
<string name="permissions_contacts_details">Per sincronizzare l\'indirizzario CardDAV con i contatti locali DAVdroid deve avere l\'accesso ai tuoi contatti.</string>
<string name="permissions_contacts_request">Richiesta autorizzazione ai contatti</string>
<string name="permissions_opentasks">Permessi OpenTasks</string>
<string name="permissions_opentasks_details">Per sincronizzazione l\'elenco attività di CalDAV con l\'elenco locale DAVdroid deve avere l\'accesso ad OpenTasks.</string>
<string name="permissions_opentasks_request">Richiesta autorizzazione ad OpenTasks</string>
<!--AddAccountActivity-->
<string name="login_title">Aggiungi account</string>
<string name="login_type_email">Accedi con indirizzo email</string>
<string name="login_email_address">Indirizzo email</string>
<string name="login_email_address_error">È necessario un indirizzo email valido</string>
<string name="login_password">Password</string>
<string name="login_password_required">Password richiesta</string>
<string name="login_type_url">Accedi con URL e nome utente</string>
<string name="login_url_must_be_http_or_https">L\'URL deve iniziare con http(s)://</string>
<string name="login_url_host_name_required">Nome host richiesto</string>
<string name="login_user_name">Nome utente</string>
<string name="login_user_name_required">Nome utente richiesto</string>
<string name="login_base_url">Base URL</string>
<string name="login_login">Login</string>
<string name="login_back">Indietro</string>
<string name="login_create_account">Crea account</string>
<string name="login_account_name">Nome account</string>
<string name="login_account_name_info">Inserisci il tuo indirizzo email come nome dell\'account in quanto Android userà il nome dell\'account nel campo ORGANIZER degli eventi creati. Non è possibile avere due account con nome uguale.</string>
<string name="login_account_contact_group_method">Metodo del contact group:</string>
<string name="login_account_name_required">Richiesto il nome dell\'account</string>
<string name="login_account_not_created">L\'account non può essere creato</string>
<string name="login_configuration_detection">Rilevazione configurazione</string>
<string name="login_querying_server">Attendere, invio richiesta al server...</string>
<string name="login_no_caldav_carddav">Impossibile trovare servizi CalDAV o CardDAV.</string>
<string name="login_view_logs">Vedi i log</string>
<!--AccountSettingsActivity-->
<string name="settings_title">Impostazioni: %s</string>
<string name="settings_authentication">Autenticazione</string>
<string name="settings_username">Nome utente</string>
<string name="settings_enter_username">Inserisci nome utente:</string>
<string name="settings_password">Password</string>
<string name="settings_password_summary">Aggiorna la password come sul tuo server.</string>
<string name="settings_enter_password">Inserisci la tua password:</string>
<string name="settings_sync">Sincronizzazione</string>
<string name="settings_sync_interval_contacts">Intervallo sincr. Contatti</string>
<string name="settings_sync_summary_manually">Solo manualmente</string>
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Ogni %d minuti e a seguito di ogni cambiamento locale</string>
<string name="settings_sync_interval_calendars">Intervallo sincr. calendari</string>
<string name="settings_sync_interval_tasks">Intervallo sincr. attività</string>
<string-array name="settings_sync_interval_seconds">
<item>-1</item>
<item>300</item>
<item>600</item>
<item>900</item>
<item>3600</item>
<item>7200</item>
<item>14400</item>
<item>86400</item>
</string-array>
<string-array name="settings_sync_interval_names">
<item>Solo manualmente</item>
<item>Ogni 5 minuti</item>
<item>Ogni 10 minuti</item>
<item>Ogni 15 minuti</item>
<item>Ogni ora</item>
<item>Ogni 2 ore</item>
<item>Ogni 4 ore</item>
<item>Una volta al giorno</item>
</string-array>
<string name="settings_sync_wifi_only">Sincr. solo tramite WiFi</string>
<string name="settings_sync_wifi_only_on">La sincronizzazione è limitata alle connessioni WiFi</string>
<string name="settings_sync_wifi_only_off">Il tipo di connessione non è preso in considerazione</string>
<string name="settings_sync_wifi_only_ssid">Restrizione sul SSID del WiFi</string>
<string name="settings_sync_wifi_only_ssid_on">La sincronizzazione avverrà solo con %s</string>
<string name="settings_sync_wifi_only_ssid_off">Verranno usate tutte le connessioni WiFi</string>
<string name="settings_sync_wifi_only_ssid_message">Inserisci il nome di una rete WiFi (SSID) per limitare la sincronizzazione solo con questa rete o lasciare in bianco per usare tutte le connessioni WiFi.</string>
<string name="settings_carddav">CardDAV</string>
<string name="settings_contact_group_method">Organizzazione dei gruppi di contatto</string>
<string-array name="settings_contact_group_method_values">
<item>GROUP_VCARDS</item>
<item>CATEGORIES</item>
</string-array>
<string-array name="settings_contact_group_method_entries">
<item>I gruppi sono VCards separate</item>
<item>I gruppi sono categorie per ogni contatto</item>
</string-array>
<string name="settings_caldav">CalDAV</string>
<string name="settings_sync_time_range_past">Limite di tempo per gli eventi trascorsi</string>
<string name="settings_sync_time_range_past_none">Verranno sincronizzati tutti gli eventi</string>
<plurals name="settings_sync_time_range_past_days">
<item quantity="one">Eventi più vecchi di un giorno saranno ignorati</item>
<item quantity="other">Eventi più vecchi di %d giorni saranno ignorati</item>
</plurals>
<string name="settings_sync_time_range_past_message">Eventi più vecchi di questo numero di giorni verranno ignorati(può anche essere 0). Lasciare in bianco per sincronizzare tutti gli eventi.</string>
<string name="settings_manage_calendar_colors">Cambia il colore del calendario</string>
<string name="settings_manage_calendar_colors_on">I colori dei calendari sono gestiti da DAVdroid</string>
<string name="settings_manage_calendar_colors_off">I colori dei calendari non sono gestiti da DAVdroid</string>
<!--collection management-->
<string name="create_addressbook">Crea indirizzario</string>
<string name="create_addressbook_display_name_hint">Il mio indirizzario</string>
<string name="create_calendar">Crea raccolta CalDAV</string>
<string name="create_calendar_display_name_hint">Mio calendario</string>
<string name="create_calendar_time_zone">Fuso orario:</string>
<string name="create_calendar_type">Tipo di raccolta:</string>
<string name="create_calendar_type_only_events">Calendario (solo eventi)</string>
<string name="create_calendar_type_only_tasks">Elenco attività (solo attività)</string>
<string name="create_calendar_type_events_and_tasks">Combinato (eventi e attività)</string>
<string name="create_collection_color">Imposta colore della raccolta</string>
<string name="create_collection_creating">Crea una raccolta</string>
<string name="create_collection_display_name">Mostra il nome (titolo) di questa raccolta:</string>
<string name="create_collection_display_name_required">Il titolo è richiesto</string>
<string name="create_collection_description">Descrizione (opzionale):</string>
<string name="create_collection_home_set">Imposta la home:</string>
<string name="create_collection_create">Crea</string>
<string name="delete_collection">Elimina raccolta</string>
<string name="delete_collection_confirm_title">Sei sicuro?</string>
<string name="delete_collection_confirm_warning">Questa raccolta (%s) e tutti i suoi dati saranno rimossi dal server.</string>
<string name="delete_collection_deleting_collection">Cancellazione della raccolta</string>
<!--ExceptionInfoFragment-->
<string name="exception">Si è verificato un errore.</string>
<string name="exception_httpexception">Si è verificato un errore HTTP.</string>
<string name="exception_ioexception">Si è verificato un errore di I/O.</string>
<string name="exception_show_details">Mostra dettagli</string>
<!--sync errors and DebugInfoActivity-->
<string name="debug_info_title">Informazioni di debug</string>
<string name="sync_error_permissions">Autorizzazioni DAVdroid</string>
<string name="sync_error_permissions_text">Autorizzazioni addizionali richieste</string>
<string name="sync_error_calendar">Sincronizzazione del calendario fallita (%s)</string>
<string name="sync_error_contacts">Sincronizzazione dell\'indirizzario fallita (%s)</string>
<string name="sync_error_tasks">Sincronizzazione delle attività fallita (%s)</string>
<string name="sync_error">Errore nel %s</string>
<string name="sync_error_http_dav">Errore del server nel %s</string>
<string name="sync_error_local_storage">Errore del database nel %s</string>
<string-array name="sync_error_phases">
<item>inizio sincronizzazione</item>
<item>controllo caratteristiche del server</item>
<item>elaborazione voci cancellate in locale</item>
<item>elaborazione voci create o modificate</item>
<item>invio voci create o modificate</item>
<item>controllo stato della sincronizzazione</item>
<item>elenco voci locali</item>
<item>elenco voci remote</item>
<item>confronto voci locali e remote</item>
<item>download voci remote</item>
<item>post-processing</item>
<item>salvataggio stato della sincronizzazione</item>
</string-array>
<string name="sync_error_unauthorized">Nome utente o password errati</string>
<!--cert4android-->
<string name="certificate_notification_connection_security">DAVdroid: sicurezza della connessione</string>
<string name="trust_certificate_unknown_certificate_found">DAVdroid ha trovato un certificato sconosciuto. Ritenerlo affidabile?</string>
</resources>
-251
View File
@@ -1,251 +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 Adresboek</string>
<string name="address_books_authority_title">Adresboeken</string>
<string name="help">Help</string>
<string name="manage_accounts">Beheer accounts</string>
<string name="please_wait">Een moment geduld...</string>
<string name="send">Verzenden</string>
<string name="homepage_url">https://davdroid.bitfire.at/?pk_campaign=davdroid-app</string>
<!--startup dialogs-->
<string name="startup_battery_optimization">Batterij optimalisatie</string>
<string name="startup_battery_optimization_message">Android kan mogelijk de DAVdroid synchronisatie stoppen na een paar dagen. Om dit te voorkomen zet u de batterij optimalisatie uit.</string>
<string name="startup_battery_optimization_disable">DAVdroid afsluiten</string>
<string name="startup_dont_show_again">Niet opnieuw weergeven</string>
<string name="startup_development_version">Voorvertoning van uitgave</string>
<string name="startup_development_version_message">Dit is ontwikkelversie %s. Onderdelen werken niet zoals verwacht. Je terugkoppeling is welkom.</string>
<string name="startup_development_version_give_feedback">Feedback geven</string>
<string name="startup_development_version_feedback_url">https://davdroid.bitfire.at/forums/?pk_campaign=davdroid-app</string>
<string name="startup_donate">Open-Source informatie</string>
<string name="startup_donate_message">We zijn blij dat je DAVdroid gebruikt, wat open-source software (GPLv3) is. Omdat de ontwikkeling van DAVdroid hard werk is en duizenden uren in beslag neemt. overweeg alstublieft een donatie.</string>
<string name="startup_donate_now">Toon donatie pagina</string>
<string name="startup_donate_later">Misschien later</string>
<string name="startup_google_play_accounts_removed">Play Store DRM fout-informatie</string>
<string name="startup_google_play_accounts_removed_message">Onder bepaalde omstandigheden, kan Play Store DRM ervoor zorgen dat accounts kwijt zijn na een herstart of na een DAVdroid update. Als dit probleem zich bij je voordoet (en alleen dan), Installeer dan \"DAVdroid JB Workaround\" vanuit de Play Store</string>
<string name="startup_google_play_accounts_removed_more_info">Meer informatie</string>
<string name="startup_opentasks_not_installed">OpenTasks niet geinstalleerd</string>
<string name="startup_opentasks_not_installed_message">De OpenTasks app is niet beschikbaar, Hierdoor is het voor DAVdroid niet mogelijk om uw taken te synchroniseren.</string>
<string name="startup_opentasks_reinstall_davdroid">Na installatie van OpenTasks dient u DAVdroid opnieuw te installeren en de accounts toe te voegen (Android bug).</string>
<string name="startup_opentasks_not_installed_install">OpenTasks installeren</string>
<!--AboutActivity-->
<string name="about_license_terms">Licentie voorwaarden</string>
<string name="about_license_info_no_warranty">Dit programma kom met ABSOLUUT GEEN GARANTIE. Het is gratis software, en je bent welkom dit te herdistribueren onder bepaalde voorwaarden.</string>
<!--global settings-->
<string name="logging_davdroid_file_logging">DAVDroid bestand loggen</string>
<string name="logging_to_external_storage">Loggen naar externe opslag: %s</string>
<string name="logging_to_external_storage_warning">Verwijder logs zo snel mogelijk</string>
<string name="logging_couldnt_create_file">Kon extern log bestand niet verwijderen: %s</string>
<string name="logging_no_external_storage">Externe opslag niet gevonden</string>
<!--AccountsActivity-->
<string name="navigation_drawer_open">Open navigatie drawer</string>
<string name="navigation_drawer_close">Sluit navigatie drawer</string>
<string name="navigation_drawer_subtitle">CalDAV/CardDav Sync adapter</string>
<string name="navigation_drawer_about">Over / Licentie</string>
<string name="navigation_drawer_settings">Instellingen</string>
<string name="navigation_drawer_news_updates">Nieuws &amp; updates</string>
<string name="navigation_drawer_external_links">Externe links</string>
<string name="navigation_drawer_website">Website</string>
<string name="navigation_drawer_faq">FAQ</string>
<string name="navigation_drawer_faq_url">https://davdroid.bitfire.at/faq/?pk_campaign=davdroid-app</string>
<string name="navigation_drawer_donate">Doneren</string>
<string name="account_list_empty">Welkom bij DAVdroid!\n\nJe kunt nu een CalDAV/CardDAv account toevoegen.</string>
<string name="accounts_global_sync_disabled">Systeembrede automatische synchronisatie is uitgeschakeld</string>
<string name="accounts_global_sync_enable">Inschakelen</string>
<!--DavService-->
<string name="dav_service_refresh_failed">Service herkenning is mislukt</string>
<string name="dav_service_refresh_couldnt_refresh">Kon de collectie lijst niet vernieuwen</string>
<!--AppSettingsActivity-->
<string name="app_settings">Instellingen</string>
<string name="app_settings_user_interface">Gebruikers interface</string>
<string name="app_settings_reset_hints">Hints resetten </string>
<string name="app_settings_reset_hints_summary">Hints die al gezien zijn opnieuw weergeven</string>
<string name="app_settings_reset_hints_success">Alle hints worden opnieuw weergegeven</string>
<string name="app_settings_connection">Verbinding</string>
<string name="app_settings_override_proxy">Proxy instellingen overschrijven</string>
<string name="app_settings_override_proxy_on">Eigen proxy instellingen gebruiken</string>
<string name="app_settings_override_proxy_off">Systeem proxy instellingen gebruiken</string>
<string name="app_settings_override_proxy_host">HTTP proxy beheerder naam</string>
<string name="app_settings_override_proxy_port">HTTP proxy poort</string>
<string name="app_settings_security">Beveiliging</string>
<string name="app_settings_distrust_system_certs">Systeem certificaten niet vertrouwen</string>
<string name="app_settings_distrust_system_certs_on">Systeem en CAs van toegevoegde gebruiker wordt niet vertrouwd</string>
<string name="app_settings_distrust_system_certs_off">Systeem en CAs van toegevoegde gebruiker wordt vertrouwd (aanbevolen)</string>
<string name="app_settings_reset_certificates">Resetten (niet) vertrouwde certificaten</string>
<string name="app_settings_reset_certificates_summary">Resetten alle bewerkte certificaten</string>
<string name="app_settings_reset_certificates_success">Alle bewerkte certificaten zijn vrijgemaakt</string>
<string name="app_settings_debug">Debuggen</string>
<string name="app_settings_log_to_external_storage">Log naar extern bestand</string>
<string name="app_settings_log_to_external_storage_on">Loggen naar externe opslag (wanneer beschikbaar)</string>
<string name="app_settings_log_to_external_storage_off">Extern bestands loggen uitgeschakeld</string>
<string name="app_settings_show_debug_info">Debug info tonen</string>
<string name="app_settings_show_debug_info_details">Bekijk/deel software configuratie details</string>
<!--AccountActivity-->
<string name="account_synchronize_now">Synchroniseer nu</string>
<string name="account_synchronizing_now">Aan het synchronizeren...</string>
<string name="account_settings">Account instellingen</string>
<string name="account_rename">Account hernoemen</string>
<string name="account_rename_new_name">Niet opgeslagen lokale informatie mag verloren gaan. Synchronisatie is noodzakelijk na hernoemen. Nieuw account naam:</string>
<string name="account_rename_rename">Hernoemen</string>
<string name="account_delete">Account verwijderen</string>
<string name="account_delete_confirmation_title">Account echt verwijderen?</string>
<string name="account_delete_confirmation_text">Alle lokale kopieën van adresboeken, agenda\'s en taken worden verwijderd.</string>
<string name="account_refresh_address_book_list">Adresboeken vernieuwen</string>
<string name="account_create_new_address_book">Maak een nieuw adresboek</string>
<string name="account_refresh_calendar_list">Agenda\'s vernieuwen</string>
<string name="account_create_new_calendar">Maak een nieuwe agenda</string>
<!--PermissionsActivity-->
<string name="permissions_title">DAVdroid rechten</string>
<string name="permissions_calendar">Agenda rechten</string>
<string name="permissions_calendar_details">Om CalDAV afspraken te synchroniseren met u agenda dient DAVdroid toegang te verkrijgen. </string>
<string name="permissions_calendar_request">Agenda rechten verkrijgen</string>
<string name="permissions_contacts">Contact rechten</string>
<string name="permissions_contacts_details">Om CalDAV afspraken te synchroniseren met u contacten dient DAVdroid toegang te verkrijgen. </string>
<string name="permissions_contacts_request">Contacten rechten verkrijgen</string>
<string name="permissions_opentasks">OpenTasks rechten</string>
<string name="permissions_opentasks_details">Om CalDAV taken te synchroniseren met uw local takenlijst dient DAVdroid toegang te hebben tot OpenTasks</string>
<string name="permissions_opentasks_request">OpenTasks rechten verkrijgen</string>
<!--AddAccountActivity-->
<string name="login_help_url">https://davdroid.bitfire.at/configuration/?pk_campaign=davdroid-app</string>
<string name="login_title">Account toevoegen</string>
<string name="login_type_email">Inloggen met e-mailadres</string>
<string name="login_email_address">Email adres</string>
<string name="login_email_address_error">Geldig email adres vereist</string>
<string name="login_password">Wachtwoord</string>
<string name="login_password_required">Wachtwoord vereist</string>
<string name="login_type_url">Inloggen met URL en gebruikersnaam</string>
<string name="login_url_must_be_http_or_https">URL moet met http(s):// beginnen</string>
<string name="login_url_host_name_required">Hostnaam vereist</string>
<string name="login_user_name">Gebruikersnaam</string>
<string name="login_user_name_required">Gebruikersnaam vereist</string>
<string name="login_base_url">Basis URL</string>
<string name="login_login">Login</string>
<string name="login_back">Terug</string>
<string name="login_create_account">Maak een account</string>
<string name="login_account_name">Accountnaam</string>
<string name="login_account_name_info">Gebruik je email adres als account naam want Android zal je account naam gebruiken als ORGANIZER veld voor gemaakte afspraken. Je kunt geen 2 accounts met dezelfde naam hebben,</string>
<string name="login_account_contact_group_method">Contact groep methode:</string>
<string name="login_account_name_required">Accountnaam vereist</string>
<string name="login_account_not_created">Account kon niet gemaakt worden.</string>
<string name="login_configuration_detection">Configuratie detectie</string>
<string name="login_querying_server">Even geduld, verzoek naar server...</string>
<string name="login_no_caldav_carddav">Kon geen CalDAV of CardDAV service vinden.</string>
<string name="login_view_logs">Bekijk logs</string>
<!--AccountSettingsActivity-->
<string name="settings_title">Instellingen: %s</string>
<string name="settings_authentication">Authenticatie</string>
<string name="settings_username">Gebruikersnaam</string>
<string name="settings_enter_username">Gebruikersnaam invoeren:</string>
<string name="settings_password">Wachtwoord</string>
<string name="settings_password_summary">Gebruik het zelfde wachtwoord als op de server.</string>
<string name="settings_enter_password">Wachtwoord invoeren:</string>
<string name="settings_sync">Synchronisatie</string>
<string name="settings_sync_interval_contacts">Contacten verversen</string>
<string name="settings_sync_summary_manually">Alleen handmatig</string>
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Elke %d minuten + meteen na wijziging</string>
<string name="settings_sync_interval_calendars">Agenda\'s verversen</string>
<string name="settings_sync_interval_tasks">Taak sync. tussentijd</string>
<string-array name="settings_sync_interval_seconds">
<item>-1</item>
<item>300</item>
<item>600</item>
<item>900</item>
<item>3600</item>
<item>7200</item>
<item>14400</item>
<item>86400</item>
</string-array>
<string-array name="settings_sync_interval_names">
<item>Alleen handmatig</item>
<item>Elke 5 minuten</item>
<item>Elke 10 minuten</item>
<item>Elke 15 minuten</item>
<item>Elk uur</item>
<item>Elke 2 uur</item>
<item>Elke 4 uur</item>
<item>Dagelijks</item>
</string-array>
<string name="settings_sync_wifi_only">Sync alleen tijdens WiFi</string>
<string name="settings_sync_wifi_only_on">Synchronisatie is voorbehouden tijdens WiFi verbindingen</string>
<string name="settings_sync_wifi_only_off">Verbinding type is niet overwogen</string>
<string name="settings_sync_wifi_only_ssid">WiFi SSID beperking</string>
<string name="settings_sync_wifi_only_ssid_on">Zal alleen synchroniseren over %s</string>
<string name="settings_sync_wifi_only_ssid_off">Alle WiFI verbindingen mogen worden gebruikt</string>
<string name="settings_sync_wifi_only_ssid_message">Type de naam van het WiFi netwerk (SSID) om synchronisatie tot dit netwerk te beperken. Leeg laten voor sync over alle netwerken.</string>
<string name="settings_carddav">CardDAV</string>
<string name="settings_contact_group_method">Contact groep methode</string>
<string-array name="settings_contact_group_method_values">
<item>GROUP_VCARDS</item>
<item>CATEGORIES</item>
</string-array>
<string-array name="settings_contact_group_method_entries">
<item>Groepen zijn apparte VCards</item>
<item>Groepen zijn per-contact categories</item>
</string-array>
<string name="settings_caldav">CalDAV</string>
<string name="settings_sync_time_range_past">Tijdslimiet verleden afspraken</string>
<string name="settings_sync_time_range_past_none">Alle afspraken worden gesynchronizeerd</string>
<plurals name="settings_sync_time_range_past_days">
<item quantity="one">Afspraken ouder dan een dag worden genegeerd</item>
<item quantity="other">Afspraken ouder dan %d dagen worden genegeerd</item>
</plurals>
<string name="settings_sync_time_range_past_message">Afspraken ouder dan dit aantal dagen worden genegeerd (mag 0 zijn). Laat leeg om alle afspraken te synchronizeren.</string>
<string name="settings_manage_calendar_colors">Agenda kleuren beheren</string>
<string name="settings_manage_calendar_colors_on">Agenda kleuren worden door DAVdroid beheerd.</string>
<string name="settings_manage_calendar_colors_off">Agenda kleuren worden niet door DAVdroid ingesteld</string>
<!--collection management-->
<string name="create_addressbook">Maak adresboek</string>
<string name="create_addressbook_display_name_hint">Mijn adresboek</string>
<string name="create_calendar">Maak CalDAV collectie</string>
<string name="create_calendar_display_name_hint">Mijn agenda</string>
<string name="create_calendar_time_zone">Tijdzone:</string>
<string name="create_calendar_type">Collectie type:</string>
<string name="create_calendar_type_only_events">Agenda (alleen afspraken)</string>
<string name="create_calendar_type_only_tasks">Takenlijst (alleen taken)</string>
<string name="create_calendar_type_events_and_tasks">Gecombineerd (afspraken en taken)</string>
<string name="create_collection_color">Stel een collectie kleur in</string>
<string name="create_collection_creating">Collectie aan het maken</string>
<string name="create_collection_display_name">Weergave naam (titel) van deze collectie:</string>
<string name="create_collection_display_name_required">Titel is vereist</string>
<string name="create_collection_description">Beschrijving (optioneel):</string>
<string name="create_collection_home_set">Begin map:</string>
<string name="create_collection_create">Maak</string>
<string name="delete_collection">Verwijder collectie</string>
<string name="delete_collection_confirm_title">Weet je het zeker?</string>
<string name="delete_collection_confirm_warning">Deze collectie (%s) en alle data zal verwijderd worden van de server.</string>
<string name="delete_collection_deleting_collection">Collectie aan het verwijderen</string>
<!--ExceptionInfoFragment-->
<string name="exception">Er is een fout opgetreden.</string>
<string name="exception_httpexception">Er is een HTTP fout opgetreden.</string>
<string name="exception_ioexception">Er is een I/O fout opgetreden.</string>
<string name="exception_show_details">Toon details</string>
<!--sync errors and DebugInfoActivity-->
<string name="debug_info_title">Debug informatie</string>
<string name="sync_error_permissions">DAVdroid rechten</string>
<string name="sync_error_permissions_text">Aanvullende rechten vereist</string>
<string name="sync_error_calendar">Agenda synchronisatie is mislukt (%s)</string>
<string name="sync_error_contacts">Adresboek synchronisatie is mislukt (%s)</string>
<string name="sync_error_tasks">Taak synchronisatie is mislukt (%s)</string>
<string name="sync_error">Fout tijdens %s</string>
<string name="sync_error_http_dav">Serverfout tijdens %s</string>
<string name="sync_error_local_storage">Database fout tijdens %s</string>
<string-array name="sync_error_phases">
<item>synchronisatie voorbereiden</item>
<item>querying mogelijkheden</item>
<item>verwerken van lokaal verwijderde data</item>
<item>voorberteiding maken/wijzigen data</item>
<item>uploaden maken/bewerken data</item>
<item>controleren syngronisatie voortgang</item>
<item>lijst lokale data</item>
<item>lijst remote data</item>
<item>vergelijken lokale/remote data</item>
<item>downloaden remote data</item>
<item>nabewerking</item>
<item>opslaan sync voortgang</item>
</string-array>
<string name="sync_error_unauthorized">Gebruikersnaam/wachtwoord onjuist</string>
<!--cert4android-->
<string name="certificate_notification_connection_security">DAVdroid: Verbinding beveiliging</string>
<string name="trust_certificate_unknown_certificate_found">Davdroid is benaderd door een onbekend certificaat. Vertrouwd u dit?</string>
</resources>
-245
View File
@@ -1,245 +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">Książka adresowa DAVdroid</string>
<string name="address_books_authority_title">Książka adresowa</string>
<string name="help">Pomoc</string>
<string name="manage_accounts">Zadządzaj kontami</string>
<string name="please_wait">Proszę czekać</string>
<string name="send">Wyślij</string>
<!--startup dialogs-->
<string name="startup_battery_optimization">Optymalizacja baterii</string>
<string name="startup_battery_optimization_message">Android może wyłączyć/zmniejszyć synchronizacje DAVdroid po kilku dniach. Aby temu zapobiec należy wyłączyć optymalizację baterii.</string>
<string name="startup_battery_optimization_disable">Wyłącz dla DAVdroid</string>
<string name="startup_dont_show_again">Nie pokazuj ponownie</string>
<string name="startup_development_version">Poprzednie wydanie</string>
<string name="startup_development_version_message">To jest wersja rozwojowa 1%s. Nie wszystko może działać zgodnie z oczekiwaniami. Twoja opinia jest mile widziana.</string>
<string name="startup_development_version_give_feedback">Przekaż opinię</string>
<string name="startup_donate">Informacje Open-Source</string>
<string name="startup_donate_message">Jesteśmy szczęśliwi, że używasz DAVdroid, który jest oprogramowaniem open-source (GPLv3). Ponieważ rozwijanie DAVdroid jest ciężką pracą i zajęło nam tysiące godzin pracy, prosimy o rozważenie darowizny.</string>
<string name="startup_donate_now">Pokaż stronę darowizny</string>
<string name="startup_donate_later">Może później</string>
<string name="startup_google_play_accounts_removed">Informacje o błędzie DRM Sklepu Play</string>
<string name="startup_google_play_accounts_removed_message">Pod pewnymi warunkami, DRM Sklepu Play może powodować, że wszystkie konta DAVdroid mogą zostać usunięte po uruchomieniu lub po uaktualnieniu DAVdroid. Jeśli jesteś dotknięty tym problemem (i tylko wtedy) należy zainstalować \"DAVdroid JB Obejście\" ze Sklepu Play.</string>
<string name="startup_google_play_accounts_removed_more_info">Więcej informacji</string>
<string name="startup_opentasks_not_installed">OpenTasks nie jest zainstalowany</string>
<string name="startup_opentasks_not_installed_message">Aplikacja Open Tasks nie jest dostępna, więc DAVdroid nie będzie mógł synchronizować listy zadań. </string>
<string name="startup_opentasks_reinstall_davdroid">Po zainstalowaniu OpenTasks konieczne jest PRZEINSTALOWANIE DAVdroid i ponowne dodanie twoich kont (błąd Androida).</string>
<string name="startup_opentasks_not_installed_install">Zainstaluj OpenTasks</string>
<!--AboutActivity-->
<string name="about_license_terms">Warunki licencji</string>
<string name="about_license_info_no_warranty">Ten program jest ABSOLUTNIE BEZ GWARANCJI. To jest wolne oprogramowanie i mile widziane jest dalsze rozpowszechnianie go pod pewnymi warunkami.</string>
<!--global settings-->
<string name="logging_davdroid_file_logging">Plik logów DAVdroid</string>
<string name="logging_to_external_storage">Logowanie do zewnątrznej pamięci: %s</string>
<string name="logging_to_external_storage_warning">Usuń logi tak szybko jak to możliwe!</string>
<string name="logging_couldnt_create_file">Nie można stworzyć zewnętrznego pliku logów: %s</string>
<string name="logging_no_external_storage">Zewnętrzna pamięci nie została naleziona</string>
<!--AccountsActivity-->
<string name="navigation_drawer_open">Otwórz menu nawigacji</string>
<string name="navigation_drawer_close">Zamknij menu nawigacji</string>
<string name="navigation_drawer_subtitle">Adapter synchronizacji CalDAV/CardDAV</string>
<string name="navigation_drawer_about">O DAVdroid / Licencja</string>
<string name="navigation_drawer_settings">Ustawienia</string>
<string name="navigation_drawer_news_updates">Nowości &amp; aktualizacje</string>
<string name="navigation_drawer_external_links">Zewnętrzne odnośniki</string>
<string name="navigation_drawer_website">Strona WWW</string>
<string name="navigation_drawer_faq">FQA</string>
<string name="navigation_drawer_donate">Dotacja</string>
<string name="account_list_empty">Witamy w DAVdroid!\n\nMożesz teraz dodać konto CalDAV/CardDAV.</string>
<string name="accounts_global_sync_disabled">Automatyczna synchronizacja dla całego systemu jest wyłączona</string>
<string name="accounts_global_sync_enable">Włącz</string>
<!--DavService-->
<string name="dav_service_refresh_failed">Wykrycie serwisu nie powiodło się</string>
<string name="dav_service_refresh_couldnt_refresh">Nie można odświeżyć listy kolekcji</string>
<!--AppSettingsActivity-->
<string name="app_settings">Ustawienia</string>
<string name="app_settings_user_interface">Interfejs użytkownika</string>
<string name="app_settings_reset_hints">Zresetuj podpowiedzi</string>
<string name="app_settings_reset_hints_summary">Ponownie włącz wskazówki, które zostały usunięte wcześniej</string>
<string name="app_settings_reset_hints_success">Wszystkie wskazówki pojawią się ponownie</string>
<string name="app_settings_connection">Łączność</string>
<string name="app_settings_override_proxy">Nadpisz ustawienia proxy</string>
<string name="app_settings_override_proxy_on">Użyj niestandardowych ustawień proxy </string>
<string name="app_settings_override_proxy_off">Użyj systemowych ustawień proxy</string>
<string name="app_settings_override_proxy_host">Nazwa hosta HTTP proxy</string>
<string name="app_settings_override_proxy_port">Port HTTP proxy</string>
<string name="app_settings_security">Bezpieczeństwo</string>
<string name="app_settings_distrust_system_certs">Usuń certyfikaty systemowe</string>
<string name="app_settings_distrust_system_certs_on">CA systemowe i użytkownika nie zostaną dodane</string>
<string name="app_settings_distrust_system_certs_off">CA systemowe i użytkownika zostaną dodane (zalecane)</string>
<string name="app_settings_reset_certificates">Zresetuj (nie)zaufane certyfikaty</string>
<string name="app_settings_reset_certificates_summary">Zresetuj wszystkie niestandardowe certyfikaty.</string>
<string name="app_settings_reset_certificates_success">Wszystkie niestandardowe certyfikaty zostały wyczyszczone</string>
<string name="app_settings_debug">Debugowanie</string>
<string name="app_settings_log_to_external_storage">Loguj to zewnętrznego pliku</string>
<string name="app_settings_log_to_external_storage_on">Logowanie do zewnętrznej pamięci (jeśli jest dostępna)</string>
<string name="app_settings_log_to_external_storage_off">Logowanie do zewnętrznego pliku jest niedostępne</string>
<string name="app_settings_show_debug_info">Pokaż informacje do debug\'owania</string>
<string name="app_settings_show_debug_info_details">Przeglądaj/udostępnij oprogramowanie i szczegóły konfiguracji </string>
<!--AccountActivity-->
<string name="account_synchronize_now">Synchronizuj teraz</string>
<string name="account_synchronizing_now">Synchronizcja w toku</string>
<string name="account_settings">Ustawienia konta</string>
<string name="account_rename">Zmień nazwę konta</string>
<string name="account_rename_new_name">Niezapisane dane lokalne mogą zostać usunięte. Ponowna synchronizacja jest wymagana po zmianie nazwy. Nowa nazwa konta:</string>
<string name="account_rename_rename">Zmień nazwę</string>
<string name="account_delete">Usuń konto</string>
<string name="account_delete_confirmation_title">Naprawdę chcesz usunąć konto?</string>
<string name="account_delete_confirmation_text">Wszystkie lokalne kopie książek adresowych, kalendarzy i list zadań zostaną usunięte.</string>
<string name="account_refresh_address_book_list">Odśwież list książek adresowych</string>
<string name="account_create_new_address_book">Stwórz nową książkę adresową</string>
<string name="account_refresh_calendar_list">Odśwież liste kalendarzy</string>
<string name="account_create_new_calendar">Stwórz nowy kalendarz</string>
<!--PermissionsActivity-->
<string name="permissions_title">Uprawnienia DAVdroid</string>
<string name="permissions_calendar">Uprawnienia kalendarza</string>
<string name="permissions_calendar_details">Aby synchronizować wydarzenia CalDav z lokalnymi kalendarzami, DAVdroid potrzebuje dostępu do twoich kalendarzy.</string>
<string name="permissions_calendar_request">Zezwól na uprawnienia kalendarza</string>
<string name="permissions_contacts">Uprawnienia kontaktów</string>
<string name="permissions_contacts_details">Aby synchronizować książki adresowe CardDAV z lokalnymi kontaktami, DAVdroid potrzebuje dostępu do twoich kontaktów.</string>
<string name="permissions_contacts_request">Zezwól na uprawnienia kontaktów</string>
<string name="permissions_opentasks">Uprawnienia OpenTasks</string>
<string name="permissions_opentasks_details">Aby synchronizować zadania CalDav z lokalnymi listami zadań, DAVdroid potrzebuje dostępu do OpenTasks.</string>
<string name="permissions_opentasks_request">Zezwól na uprawnienia OpenTasks</string>
<!--AddAccountActivity-->
<string name="login_title">Dodaj konto</string>
<string name="login_type_email">Logowanie za pomocą adresu e-mail</string>
<string name="login_email_address">Adres e-mail</string>
<string name="login_email_address_error">Wymanagny poprawny adres e-mail</string>
<string name="login_password">Hasło</string>
<string name="login_password_required">Wymagane hasło</string>
<string name="login_type_url">Logowanie za pomocą adresu URL i nazwy użytkownika</string>
<string name="login_url_must_be_http_or_https">URL musi zaczynać się z http(s)://</string>
<string name="login_url_host_name_required">Wymagana nazwa hosta</string>
<string name="login_user_name">Nazwa użytkownika</string>
<string name="login_user_name_required">Wymagana nazwa użtkonika</string>
<string name="login_base_url">Podstawowy URL</string>
<string name="login_login">Zaloguj</string>
<string name="login_back">Wróć</string>
<string name="login_create_account">Stwórz konto</string>
<string name="login_account_name">Nazwa konta</string>
<string name="login_account_name_info">Użyj swojego adresu e-mail jako nazwy konta, ponieważ Android będzie używał nazwy konta jako pola ORGANIZATOR dla wydarzeń, które stworzysz. Nie możesz posiadać dwóch kont o takiej samej nazwie.</string>
<string name="login_account_contact_group_method">Metoda grupowania kontaktów:</string>
<string name="login_account_name_required">Wymagana nazwa konta</string>
<string name="login_account_not_created">Konto nie mogło zostać stworzone</string>
<string name="login_configuration_detection">Wykrywanie konfiguracji</string>
<string name="login_querying_server">Proszę czekać, odpytywanie serwera...</string>
<string name="login_no_caldav_carddav">Nie można znaleźć usługi CalDAV lub CardDAV.</string>
<string name="login_view_logs">Pokaż logi</string>
<!--AccountSettingsActivity-->
<string name="settings_title">Ustawienia: %s</string>
<string name="settings_authentication">Uwierzytelnianie</string>
<string name="settings_username">Nazwa użytkownika</string>
<string name="settings_enter_username">Wpisz nazwe użytkownika:</string>
<string name="settings_password">Hasło</string>
<string name="settings_password_summary">Zaktualizuj hasło zgodnie z serwerem.</string>
<string name="settings_enter_password">Wpisz hasło:</string>
<string name="settings_sync">Synchronizacja</string>
<string name="settings_sync_interval_contacts">Okres synchronizacji kontktów</string>
<string name="settings_sync_summary_manually">Tylko ręcznie</string>
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Co %d minut oraz natychmiast przy zmianach lokalnych</string>
<string name="settings_sync_interval_calendars">Okres synchronizacji kalendarzy</string>
<string name="settings_sync_interval_tasks">Okres synchronizacji list zadań</string>
<string-array name="settings_sync_interval_seconds">
<item>-1</item>
<item>300</item>
<item>600</item>
<item>900</item>
<item>3600</item>
<item>7200</item>
<item>14400</item>
<item>86400</item>
</string-array>
<string-array name="settings_sync_interval_names">
<item>Tylko ręcznie</item>
<item>Co 5 minut</item>
<item>Co 10 minut</item>
<item>Co 15 minut</item>
<item>Co godzinę</item>
<item>Co 2 godziny</item>
<item>Co 4 godziny</item>
<item>Raz dziennie</item>
</string-array>
<string name="settings_sync_wifi_only">Synchronizuj tylko przez WiFi</string>
<string name="settings_sync_wifi_only_on">Synchronizacja jest ograniczony do połączeń WiFi</string>
<string name="settings_sync_wifi_only_off">Rodzaj połączenia nie jest brany pod uwagę</string>
<string name="settings_sync_wifi_only_ssid">Organicznie WiFi SSID</string>
<string name="settings_sync_wifi_only_ssid_on">Będzie synchronizować tylko przez %s.</string>
<string name="settings_sync_wifi_only_ssid_off">Wszystkie połączenia WiFi mogą zostać wykorzystane</string>
<string name="settings_sync_wifi_only_ssid_message">Wprowadź nazwę sieci WiFi (SSID), aby ograniczyć synchronizację do tej sieci lub pozostaw puste dla wszystkich połączeń WiFi.</string>
<string name="settings_carddav">CardDAV</string>
<string name="settings_contact_group_method">Metoda grupowania kontaktów</string>
<string-array name="settings_contact_group_method_values">
<item>GROUP_VCARDS</item>
<item>CATEGORIES</item>
</string-array>
<string name="settings_caldav">CalDAV</string>
<string name="settings_sync_time_range_past">Limit czasowy przeszłych wydarzeń</string>
<string name="settings_sync_time_range_past_none">Wszystkie wydarzenia zostaną synchronizowane</string>
<plurals name="settings_sync_time_range_past_days">
<item quantity="one">Wydarzenia starsze niż jeden dzień zostaną zignorowane.</item>
<item quantity="few">Wydarzenia starsze niż %d dni zostaną zignorowane.</item>
<item quantity="many">Wydarzenia starsze niż %d dni zostaną zignorowane.</item>
<item quantity="other">Wydarzenia starsze niż %d dni zostaną zignorowane.</item>
</plurals>
<string name="settings_sync_time_range_past_message">Wydarzenia, które są starsze niż podana liczba dni zostaną zignorowane (może być 0). Zostaw puste, aby synchronizować wszystkie wydarzenia.</string>
<string name="settings_manage_calendar_colors">Zarządzaj kolorami kalendarza</string>
<string name="settings_manage_calendar_colors_on">Kolory kalendarza są zarządzane przez DAVdroid</string>
<string name="settings_manage_calendar_colors_off">Kolory kalendarze nie są ustawiane przez DAVdroid</string>
<!--collection management-->
<string name="create_addressbook">Stwórz książkę adresową</string>
<string name="create_addressbook_display_name_hint">Moja książka adresowa</string>
<string name="create_calendar">Stwórz kolekcje CalDAV</string>
<string name="create_calendar_display_name_hint">Mój kalendarz</string>
<string name="create_calendar_time_zone">Strefa czasowa:</string>
<string name="create_calendar_type">Typ kolekcji:</string>
<string name="create_calendar_type_only_events">Kalendarz (tylko wydarzenia)</string>
<string name="create_calendar_type_only_tasks">Lista zadań (tylko zadań)</string>
<string name="create_calendar_type_events_and_tasks">Połączone (wydarzenia i zadania)</string>
<string name="create_collection_color">Ustaw kolor kolekcji</string>
<string name="create_collection_creating">Tworzenie kolekcji</string>
<string name="create_collection_display_name">Nazwa wyświetlana (tytuł) kolekcji:</string>
<string name="create_collection_display_name_required">Tytuł jest wymagany</string>
<string name="create_collection_description">Opis (opcjoalnie)</string>
<string name="create_collection_home_set">Ustaw początek:</string>
<string name="create_collection_create">Stwórz</string>
<string name="delete_collection">Usuń kolekcje</string>
<string name="delete_collection_confirm_title">Jesteś pewien?</string>
<string name="delete_collection_confirm_warning">Kolekcja (%s) i jej wszystkie dane zostaną usunięte z serwera.</string>
<string name="delete_collection_deleting_collection">Usuwanie kolekcji</string>
<!--ExceptionInfoFragment-->
<string name="exception">Wystąpił błąd.</string>
<string name="exception_httpexception">Wystąpił błąd HTTP.</string>
<string name="exception_ioexception">Wystąpił błąd I/O.</string>
<string name="exception_show_details">Pokaż szczegóły</string>
<!--sync errors and DebugInfoActivity-->
<string name="debug_info_title">Informacje debugowe</string>
<string name="sync_error_permissions">Uprawnienia DAVdroid</string>
<string name="sync_error_permissions_text">Wymagane dodatkowe uprawnienia</string>
<string name="sync_error_calendar">Synchronizacja kalendarza nie powiodała się (%s)</string>
<string name="sync_error_contacts">Synchronizacja książki adresowej nie powiodała się (%s)</string>
<string name="sync_error_tasks">Synchronizacja zadań nie powiodała się (%s)</string>
<string name="sync_error">Błąd podczas %s</string>
<string name="sync_error_http_dav">Błąd servera podczas %s</string>
<string name="sync_error_local_storage">Bład bazy danych podczas %s</string>
<string-array name="sync_error_phases">
<item>przygotowanie synchronizacji</item>
<item>odpytywanie możliwości</item>
<item>przetwarzanie lokalnie usuniętych wpisów</item>
<item>przygotowanie stworzonych/zmodyfikowanych wpisów</item>
<item>wysyłanie stworzonych/zmodyfikowanych wpisów</item>
<item>sprawdzanie stanu synchronizacji</item>
<item>listowanie lokalnych wpisów</item>
<item>listowanie zdalnych wpisów</item>
<item>porównywanie lokalnych/zdalnych wpisów</item>
<item>pobieranie zdalnych wpisów</item>
<item>przetwarzanie końcowe</item>
<item>zapisywanie stanu synchronizacji</item>
</string-array>
<string name="sync_error_unauthorized">Błędna nazwa użytkownika lub hasło</string>
<!--cert4android-->
<string name="certificate_notification_connection_security">DAVdroid: Bezpieczeństwo połączenia</string>
<string name="trust_certificate_unknown_certificate_found">DAVdroid napotkał nieznany certyfikat. Czy chcesz go dodać?</string>
</resources>
@@ -1,247 +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">Livro de endereços DAVdroid</string>
<string name="address_books_authority_title">Livros de endereços</string>
<string name="help">Ajuda</string>
<string name="manage_accounts">Gerenciar contas</string>
<string name="please_wait">Por favor, aguarde...</string>
<string name="send">Enviar</string>
<!--startup dialogs-->
<string name="startup_battery_optimization">Otimização da bateria</string>
<string name="startup_battery_optimization_message">O Android pode desativar/reduzir a sincronização do DAVdroid depois de alguns dias. Para evitar isso, desligue a otimização da bateria.</string>
<string name="startup_battery_optimization_disable">Desligar para o DAVdroid</string>
<string name="startup_dont_show_again">Não mostrar novamente</string>
<string name="startup_development_version">Preview Release</string>
<string name="startup_development_version_message">Esta é uma versão de desenvolvimento do %s. Alguma coisa pode não funcionar como esperado. Suas sugestões e comentários são bem-vindos.</string>
<string name="startup_development_version_give_feedback">Enviar sugestões</string>
<string name="startup_donate">Informação sobre Código Aberto</string>
<string name="startup_donate_message">Estamos felizes que você usa o DAVdroid, um software de código aberto (GPLv3). O desenvolvimento do DAVdroid é trabalhoso e consome muitas horas de trabalho. Por esse motivo, considere fazer uma doação.</string>
<string name="startup_donate_now">Mostrar a página de doações</string>
<string name="startup_donate_later">Talvez depois</string>
<string name="startup_google_play_accounts_removed">Informação sobre o erro de DRM da Play Store</string>
<string name="startup_google_play_accounts_removed_message">Sob certas condições, o DRM da Play Store pode fazer com que todas as contas DAVdroid sejam perdidas depois de uma reinicialização ou atualização do DAVdroid. Se você for afetado por esse problema, instale o \"DAVdroid JB Workaround\" a partir da Play Store.</string>
<string name="startup_google_play_accounts_removed_more_info">Mais informações</string>
<string name="startup_opentasks_not_installed">O OpenTasks não está instalado</string>
<string name="startup_opentasks_not_installed_message">O aplicativo OpenTasks não está disponível, não sendo possível sincronizar as listas de tarefas pelo DAVdroid.</string>
<string name="startup_opentasks_reinstall_davdroid">Depois da instalação do OpenTasks, torna-se necessário REINSTALAR o DAVdroid e adicionar suas contas novamente (erro do Android).</string>
<string name="startup_opentasks_not_installed_install">Instalar o OpenTasks</string>
<!--AboutActivity-->
<string name="about_license_terms">Termos da Licença</string>
<string name="about_license_info_no_warranty">Este programa é distribuído SEM NENHUMA GARANTIA. Ele é software livre e pode ser redistribuído sob algumas condições.</string>
<!--global settings-->
<string name="logging_davdroid_file_logging">Registro do arquivo do DAVdroid</string>
<string name="logging_to_external_storage">Registrando no arquivo externo: %s</string>
<string name="logging_to_external_storage_warning">Excluir os registros assim que possível!</string>
<string name="logging_couldnt_create_file">Não foi possível criar o arquivo de registro externo: %s</string>
<string name="logging_no_external_storage">Armazenamento externo não encontrado</string>
<!--AccountsActivity-->
<string name="navigation_drawer_open">Abrir a gaveta de navegação</string>
<string name="navigation_drawer_close">Fechar gaveta de navegação</string>
<string name="navigation_drawer_subtitle">Sincronização de CalDAV/CardDAV</string>
<string name="navigation_drawer_about">Sobre / Licença</string>
<string name="navigation_drawer_settings">Configurações</string>
<string name="navigation_drawer_news_updates">Novidades e atualizações</string>
<string name="navigation_drawer_external_links">Links externos</string>
<string name="navigation_drawer_website">Site na Web</string>
<string name="navigation_drawer_faq">Perguntas fequentes</string>
<string name="navigation_drawer_donate">Doações</string>
<string name="account_list_empty">Bem-vindo ao DAVdroid!\n\nVocê pode adicionar uma conta CalDAV/CardDAV agora.</string>
<string name="accounts_global_sync_disabled">A sincronização automática pelo sistema está desativada</string>
<string name="accounts_global_sync_enable">Ativar</string>
<!--DavService-->
<string name="dav_service_refresh_failed">Falha na detecção do serviço</string>
<string name="dav_service_refresh_couldnt_refresh">Não foi possível atualizar a lista da coleção</string>
<!--AppSettingsActivity-->
<string name="app_settings">Configurações</string>
<string name="app_settings_user_interface">Interface de usuário</string>
<string name="app_settings_reset_hints">Restaurar sugestões</string>
<string name="app_settings_reset_hints_summary">Restaura as sugestões que foram descartadas anteriormente</string>
<string name="app_settings_reset_hints_success">Todas as sugestões serão exibidas novamente</string>
<string name="app_settings_connection">Conexão</string>
<string name="app_settings_override_proxy">Substituir as configurações de proxy</string>
<string name="app_settings_override_proxy_on">Usar configurações de proxy personalizadas</string>
<string name="app_settings_override_proxy_off">Usar configurações de proxy padrão do sistema</string>
<string name="app_settings_override_proxy_host">Nome do servidor proxy HTTP</string>
<string name="app_settings_override_proxy_port">Porta do proxy HTTP</string>
<string name="app_settings_security">Segurança</string>
<string name="app_settings_distrust_system_certs">Desconfiar dos certificados de sistema</string>
<string name="app_settings_distrust_system_certs_on">ACs adicionadas pelo usuário e pelo sistema não serão confiáveis</string>
<string name="app_settings_distrust_system_certs_off">ACs adicionadas pelo usuário e pelo sistema serão confiáveis (recomendado)</string>
<string name="app_settings_reset_certificates">Restaurar certificados não-confiáveis</string>
<string name="app_settings_reset_certificates_summary">Restaura a confiança de todos os certificados personalizados</string>
<string name="app_settings_reset_certificates_success">Todos os certificados personalizados foram restaurados</string>
<string name="app_settings_debug">Depuração</string>
<string name="app_settings_log_to_external_storage">Registrar em arquivo externo</string>
<string name="app_settings_log_to_external_storage_on">Registrando no armazenamento externo (se disponível)</string>
<string name="app_settings_log_to_external_storage_off">O registro em arquivo externo está desativado</string>
<string name="app_settings_show_debug_info">Mostrar informações de depuração</string>
<string name="app_settings_show_debug_info_details">Exibe/compartilha o software e os detalhes da configuração</string>
<!--AccountActivity-->
<string name="account_synchronize_now">Sincronizar agora</string>
<string name="account_synchronizing_now">Sincronizando</string>
<string name="account_settings">Configurações da conta</string>
<string name="account_rename">Renomear conta</string>
<string name="account_rename_new_name">Dados locais que não foram salvos podem ser descartados. É necessário efetuar uma nova sincronização após renomear. Novo nome da conta:</string>
<string name="account_rename_rename">Renomear</string>
<string name="account_delete">Excluir conta</string>
<string name="account_delete_confirmation_title">Deseja excluir a conta?</string>
<string name="account_delete_confirmation_text">Todas as cópias locais dos livros de endereços, calendários e listas de tarefas serão excluídas.</string>
<string name="account_refresh_address_book_list">Atualizar lista de livros de endereços</string>
<string name="account_create_new_address_book">Criar novo livro de endereços</string>
<string name="account_refresh_calendar_list">Atualizar lista de calendários</string>
<string name="account_create_new_calendar">Criar novo calendário</string>
<!--PermissionsActivity-->
<string name="permissions_title">Permissões do DAVdroid</string>
<string name="permissions_calendar">Permissões do calendário</string>
<string name="permissions_calendar_details">Para sincronizar os eventos CalDAV com seus calendários locais, o DAVdroid precisa acessar seus calendários.</string>
<string name="permissions_calendar_request">Solicitar permissão do calendário</string>
<string name="permissions_contacts">Permissões dos contados</string>
<string name="permissions_contacts_details">Para sincronizar livros de endereços CardDAV com seus contatos locais, o DAVdroid precisa acessar seus contatos.</string>
<string name="permissions_contacts_request">Solicitar permissão dos contatos</string>
<string name="permissions_opentasks">Permissões do OpenTasks</string>
<string name="permissions_opentasks_details">Para sincronizar tarefas CalDAV com suas listas de tarefas locais, o DAVdroid precisa acessar o OpenTasks.</string>
<string name="permissions_opentasks_request">Solicitar permissão do OpenTasks</string>
<!--AddAccountActivity-->
<string name="login_title">Adicionar conta</string>
<string name="login_type_email">Autenticação com endereço de e-mail</string>
<string name="login_email_address">Endereço de e-mail</string>
<string name="login_email_address_error">É necessário um e-mail válido</string>
<string name="login_password">Senha</string>
<string name="login_password_required">É necessário uma senha</string>
<string name="login_type_url">Autenticação com usuário e URL</string>
<string name="login_url_must_be_http_or_https">A URL deve começar com http(s)://</string>
<string name="login_url_host_name_required">É necessário um nome de máquina</string>
<string name="login_user_name">Usuário</string>
<string name="login_user_name_required">É necessário um nome de usuário</string>
<string name="login_base_url">URL base</string>
<string name="login_login">Autenticar</string>
<string name="login_back">Voltar</string>
<string name="login_create_account">Criar conta</string>
<string name="login_account_name">Nome da conta</string>
<string name="login_account_name_info">Use seu endereço de e-mail como nome da conta porque o Android irá usar esse nome como campo AGENDA nos eventos que você criar. Não é possível ter duas contas com o mesmo nome.</string>
<string name="login_account_contact_group_method">Método do grupo Contato:</string>
<string name="login_account_name_required">É necessário um nome de conta</string>
<string name="login_account_not_created">A conta não pôde ser criada</string>
<string name="login_configuration_detection">Detecção de configuração</string>
<string name="login_querying_server">Aguarde, procurando servidor...</string>
<string name="login_no_caldav_carddav">Não foi possível encontrar o serviço CalDAV ou CardDAV.</string>
<string name="login_view_logs">Exibir registros</string>
<!--AccountSettingsActivity-->
<string name="settings_title">Configurações: %s</string>
<string name="settings_authentication">Autenticação</string>
<string name="settings_username">Nome do usuário</string>
<string name="settings_enter_username">Digite o nome do usuário:</string>
<string name="settings_password">Senha</string>
<string name="settings_password_summary">Atualize a senha de acordo com seu servidor</string>
<string name="settings_enter_password">Digite sua senha:</string>
<string name="settings_sync">Sincronização</string>
<string name="settings_sync_interval_contacts">Intervalo sinc. de contatos</string>
<string name="settings_sync_summary_manually">Apenas manualmente</string>
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">A cada %d minutos + imediatamente nas alterações locais</string>
<string name="settings_sync_interval_calendars">Intervalo sinc. de calendários</string>
<string name="settings_sync_interval_tasks">Intervalo sinc. de tarefas</string>
<string-array name="settings_sync_interval_seconds">
<item>-1</item>
<item>300</item>
<item>600</item>
<item>900</item>
<item>3600</item>
<item>7200</item>
<item>14400</item>
<item>86400</item>
</string-array>
<string-array name="settings_sync_interval_names">
<item>Apenas manualmente</item>
<item>A cada 5 minutos</item>
<item>A cada 10 minutos</item>
<item>A cada 15 minutos</item>
<item>A cada hora</item>
<item>A cada 2 horas</item>
<item>A cada 4 horas</item>
<item>Uma vez por dia</item>
</string-array>
<string name="settings_sync_wifi_only">Sincronizar apenas por Wi-Fi</string>
<string name="settings_sync_wifi_only_on">Sincronização restrita a conexões Wi-Fi</string>
<string name="settings_sync_wifi_only_off">O tipo de conexão não é considerado</string>
<string name="settings_sync_wifi_only_ssid">Restrição de SSID Wi-Fi</string>
<string name="settings_sync_wifi_only_ssid_on">Sincronizará apenas com %s</string>
<string name="settings_sync_wifi_only_ssid_off">Qualquer conexão Wi-Fi pode ser utilizada</string>
<string name="settings_sync_wifi_only_ssid_message">Informe o nome da rede Wi-Fi (SSID) que será usada para sincronização ou deixe em branco para usar qualquer conexão Wi-Fi.</string>
<string name="settings_carddav">CardDAV</string>
<string name="settings_contact_group_method">Método do grupo Contato</string>
<string-array name="settings_contact_group_method_values">
<item>GROUP_VCARDS</item>
<item>CATEGORIES</item>
</string-array>
<string-array name="settings_contact_group_method_entries">
<item>Grupos são VCards separados</item>
<item>Grupos são categorias por contato</item>
</string-array>
<string name="settings_caldav">CalDAV</string>
<string name="settings_sync_time_range_past">Limite de tempo para eventos passados</string>
<string name="settings_sync_time_range_past_none">Todos os eventos serão sincronizados</string>
<plurals name="settings_sync_time_range_past_days">
<item quantity="one">Os eventos que ocorreram a mais de um dia serão ignorados</item>
<item quantity="other">Eventos que ocorreram a mais de %d dias serão ignorados</item>
</plurals>
<string name="settings_sync_time_range_past_message">Os eventos que ocorreram antes desse número de dias serão ignorados (pode ser 0). Deixe em branco para sincronizar todos os eventos.</string>
<string name="settings_manage_calendar_colors">Gerenciar cores dos calendários</string>
<string name="settings_manage_calendar_colors_on">Cores dos calendários definidas pelo DAVdroid</string>
<string name="settings_manage_calendar_colors_off">Cores dos calendários não definidas pelo DAVdroid</string>
<!--collection management-->
<string name="create_addressbook">Criar livro de endereços</string>
<string name="create_addressbook_display_name_hint">Meu livro de endereços</string>
<string name="create_calendar">Criar coleção CalDAV</string>
<string name="create_calendar_display_name_hint">Meu calendário</string>
<string name="create_calendar_time_zone">Fuso horário:</string>
<string name="create_calendar_type">Tipo de coleção:</string>
<string name="create_calendar_type_only_events">Calendário (apenas eventos)</string>
<string name="create_calendar_type_only_tasks">Lista de tarefas (apenas tarefas)</string>
<string name="create_calendar_type_events_and_tasks">Combinado (eventos e tarefas)</string>
<string name="create_collection_color">Definir a cor da coleção</string>
<string name="create_collection_creating">Criando a coleção</string>
<string name="create_collection_display_name">Mostrar o nome (título) desta coleção:</string>
<string name="create_collection_display_name_required">É necessário um título</string>
<string name="create_collection_description">Descrição (opcional):</string>
<string name="create_collection_home_set">Indique o endereço:</string>
<string name="create_collection_create">Criar</string>
<string name="delete_collection">Excluir coleção</string>
<string name="delete_collection_confirm_title">Tem certeza?</string>
<string name="delete_collection_confirm_warning">Esta coleção (%s) e todos os seus dados serão removidos do servidor.</string>
<string name="delete_collection_deleting_collection">Excluindo coleção</string>
<!--ExceptionInfoFragment-->
<string name="exception">Ocorreu um erro.</string>
<string name="exception_httpexception">Ocorreu um erro de HTTP.</string>
<string name="exception_ioexception">Ocorreu um erro de leitura/gravação.</string>
<string name="exception_show_details">Mostrar detalhes</string>
<!--sync errors and DebugInfoActivity-->
<string name="debug_info_title">Informações de depuração</string>
<string name="sync_error_permissions">Permissões do DAVdroid</string>
<string name="sync_error_permissions_text">É necessário permissões adicionais</string>
<string name="sync_error_calendar">Falha na sincronização do calendário (%s)</string>
<string name="sync_error_contacts">Falha na sincronização do livro de endereços (%s)</string>
<string name="sync_error_tasks">Falha na sincronização das tarefas (%s)</string>
<string name="sync_error">Erro ao %s</string>
<string name="sync_error_http_dav">Erro do servidor ao %s</string>
<string name="sync_error_local_storage">Erro do banco de dados ao %s</string>
<string-array name="sync_error_phases">
<item>preparando sincronização</item>
<item>procurando habilidades</item>
<item>processando os itens excluídos localmente</item>
<item>preparando os itens criados/modificados</item>
<item>enviando os itens criados/modificados</item>
<item>verificando o estado da sincronização</item>
<item>listando os itens locais</item>
<item>listando os itens remotos</item>
<item>comparando os itens locais/remotos</item>
<item>baixando os itens remotos</item>
<item>pós-processamento</item>
<item>salvando o estado da sincronização</item>
</string-array>
<string name="sync_error_unauthorized">Usuário/senha incorreto</string>
<!--cert4android-->
<string name="certificate_notification_connection_security">DAVdroid: Segurança da conexão</string>
<string name="trust_certificate_unknown_certificate_found">O DAVdroid encontrou um certificado desconhecido. Deseja torná-lo confiável?</string>
</resources>
@@ -1,53 +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">Ajuda</string>
<!--startup dialogs-->
<!--AboutActivity-->
<!--global settings-->
<!--AccountsActivity-->
<!--DavService-->
<!--AppSettingsActivity-->
<!--AccountActivity-->
<!--PermissionsActivity-->
<!--AddAccountActivity-->
<string name="login_type_email">Login com seu enderço de email</string>
<string name="login_type_url">Login com URL e nome do usuário</string>
<!--AccountSettingsActivity-->
<string name="settings_authentication">Autenticação</string>
<string name="settings_username">Nome de usuário</string>
<string name="settings_enter_username">Informe o nome de usuário:</string>
<string name="settings_password">Senha</string>
<string name="settings_password_summary">Atualize sua denha de acordo com a do seu servidor.</string>
<string name="settings_enter_password">Informe sua senha:</string>
<string name="settings_sync">Sincronização</string>
<string name="settings_sync_interval_contacts">Intervalo de sincronização dos contatos</string>
<string name="settings_sync_summary_manually">Manualmente apenas</string>
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">A cada %d minutos + imediatamente em alterações locais</string>
<string name="settings_sync_interval_calendars">Intervalo de sincronização do calendário</string>
<string-array name="settings_sync_interval_seconds">
<item>-1</item>
<item>300</item>
<item>600</item>
<item>900</item>
<item>3600</item>
<item>7200</item>
<item>14400</item>
<item>86400</item>
</string-array>
<string-array name="settings_sync_interval_names">
<item>Apenas manualmente</item>
<item>A cada 5 minutos</item>
<item>A cada 10 minutos</item>
<item>A cada 15 minutos</item>
<item>A cada hora</item>
<item>A cada 2 horas</item>
<item>A cada 4 horas</item>
<item>Diáriamente</item>
</string-array>
<!--collection management-->
<!--ExceptionInfoFragment-->
<!--sync errors and DebugInfoActivity-->
<!--cert4android-->
</resources>
-239
View File
@@ -1,239 +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">Помощь</string>
<string name="manage_accounts">Управление аккаунтами</string>
<string name="please_wait">Пожалуйста подождите...</string>
<string name="send">Отправить</string>
<!--startup dialogs-->
<string name="startup_battery_optimization">Оптимизация батареи</string>
<string name="startup_battery_optimization_message">Андроид может отключить/уменьшить синхронизацию DAVdroid через несколько дней. Чтобы этого не произошло, выключите оптимизацию батареи.</string>
<string name="startup_battery_optimization_disable">Отключить для DAVdroid</string>
<string name="startup_dont_show_again">Больше не показывать</string>
<string name="startup_development_version_give_feedback">Оставить отзыв</string>
<string name="startup_donate">Open-Source информация</string>
<string name="startup_donate_message">Мы рады, что вы используете DAVdroid, который является программным обеспечением с открытым исходным кодом (GPLv3). Разработка DAVdroid является сложной задачей, потребовавшей от нас тысяч рабочих часов. Пожалуйста, рассмотрите возможность поддержать проект.</string>
<string name="startup_donate_now">Поддержать проект</string>
<string name="startup_donate_later">Возможно, позже</string>
<string name="startup_google_play_accounts_removed">Информация об ошибке в Play Store DRM</string>
<string name="startup_google_play_accounts_removed_message">При определённых условиях Play Store DRM может стать причиной потери всех DAVdroid аккаунтов после перезагрузки устройства или после обновления DAVdroid. Если Вы столкнулись с этой проблемой (и только в этом случае), установите \"DAVdroid JB Workaround\" из Play Store.</string>
<string name="startup_google_play_accounts_removed_more_info">Дополнительно</string>
<string name="startup_opentasks_not_installed">OpenTasks не установлен</string>
<string name="startup_opentasks_not_installed_message">OpenTasks недоступен, DAVdroid не сможет синхронизировать список задач</string>
<string name="startup_opentasks_reinstall_davdroid">После установки OpenTasks необходимо ПЕРЕУСТАНОВИТЬ DAVdroid и добавить Ваши аккаунты ещё раз (ошибка Android).</string>
<string name="startup_opentasks_not_installed_install">Установить OpenTasks</string>
<!--AboutActivity-->
<string name="about_license_terms">Лицензионное соглашение</string>
<string name="about_license_info_no_warranty">Эта программа поставляется АБСОЛЮТНО БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ. Это свободная программа, и вы приглашаетесь повторно распространять ее при определенных условиях.</string>
<!--global settings-->
<string name="logging_davdroid_file_logging">Файл логов DAVdroid</string>
<string name="logging_to_external_storage">Сохранение логов во внешнем хранилище: %s</string>
<string name="logging_to_external_storage_warning">Удалять логи насколько возможно быстро!</string>
<string name="logging_couldnt_create_file">Не удалось создать внешний лог файл: %s</string>
<string name="logging_no_external_storage">Внешнее хранилище не найдено</string>
<!--AccountsActivity-->
<string name="navigation_drawer_open">Открыть панель навигации</string>
<string name="navigation_drawer_close">Закрыть панель навигации</string>
<string name="navigation_drawer_subtitle">Адаптер синхронизации CalDAV/CardDAV</string>
<string name="navigation_drawer_about">О программе / Лицензия</string>
<string name="navigation_drawer_settings">Настройки</string>
<string name="navigation_drawer_news_updates">Новости и обновления</string>
<string name="navigation_drawer_external_links">Внешние ссылки</string>
<string name="navigation_drawer_website">Веб сайт</string>
<string name="navigation_drawer_faq">ЧАВО</string>
<string name="navigation_drawer_donate">Пожертвовать</string>
<string name="account_list_empty">Вас приветствует DAVdroid\n\nМожете добавить CalDAV/CardDAV аккаунт сейчас.</string>
<!--DavService-->
<string name="dav_service_refresh_failed">Не удалось обнаружить сервисы</string>
<string name="dav_service_refresh_couldnt_refresh">Невозможно обновить список коллекций</string>
<!--AppSettingsActivity-->
<string name="app_settings">Настройки</string>
<string name="app_settings_user_interface">Интерфейс пользователя</string>
<string name="app_settings_reset_hints">Включить подсказки</string>
<string name="app_settings_reset_hints_summary">Включить подсказки, которые были отключены ранее</string>
<string name="app_settings_reset_hints_success">Все подсказки будут показаны снова</string>
<string name="app_settings_connection">Соединение</string>
<string name="app_settings_override_proxy">Переопределить настройки прокси-сервера</string>
<string name="app_settings_override_proxy_on">Использовать пользовательские настройки прокси-сервера</string>
<string name="app_settings_override_proxy_off">Использовать системные настройки прокси-сервера</string>
<string name="app_settings_override_proxy_host">Имя хоста HTTP прокси-сервера</string>
<string name="app_settings_override_proxy_port">Порт HTTP прокси-сервера</string>
<string name="app_settings_security">Безопасность</string>
<string name="app_settings_distrust_system_certs">Не доверять системным сертификатам</string>
<string name="app_settings_distrust_system_certs_on">Не доверять системным и добавленным пользователем CA</string>
<string name="app_settings_distrust_system_certs_off">Доверять системным и добавленным пользователем CA (рекомендуется)</string>
<string name="app_settings_reset_certificates">Сброс (не)доверенных сертификатов</string>
<string name="app_settings_reset_certificates_summary">Отменить доверие ко всем пользовательским сертификатам</string>
<string name="app_settings_reset_certificates_success">Все пользовательские сертификаты были очищены</string>
<string name="app_settings_debug">Отладка</string>
<string name="app_settings_log_to_external_storage">Сохранять лог во внешний файл</string>
<string name="app_settings_log_to_external_storage_on">Сохранение логов во внешнем хранилище (если доступно)</string>
<string name="app_settings_log_to_external_storage_off">Сохранение логов во внешний файл отключено</string>
<string name="app_settings_show_debug_info">Отладочная информация</string>
<string name="app_settings_show_debug_info_details">Просмотреть/поделиться программой и настройками</string>
<!--AccountActivity-->
<string name="account_synchronize_now">Синхронизировать</string>
<string name="account_synchronizing_now">Синхронизация</string>
<string name="account_settings">Настройки аккаунта</string>
<string name="account_rename">Переименовать аккаунт</string>
<string name="account_rename_new_name">Несохранённые локальные данные могут быть потеряны. Требуется выполнить синхронизацию после переименования. Новое имя аккаунта:</string>
<string name="account_rename_rename">Переименовать</string>
<string name="account_delete">Удалить аккаунт</string>
<string name="account_delete_confirmation_title">Вы действительно хотите удалить аккаунт?</string>
<string name="account_delete_confirmation_text">Все локальные копии контактов, календарей и задач будут удалены.</string>
<string name="account_refresh_address_book_list">Обновить список адресных книг</string>
<string name="account_create_new_address_book">Создать новую адресную книгу</string>
<string name="account_refresh_calendar_list">Обновить календарь</string>
<string name="account_create_new_calendar">Создать новый календарь</string>
<!--PermissionsActivity-->
<string name="permissions_title">Разрешения DAVdroid</string>
<string name="permissions_calendar">Разрешения для календаря</string>
<string name="permissions_calendar_details">Для синхронизации CalDAV событий с Вашими локальными календарями DAVdroid должен иметь доступ к Вашим календарям.</string>
<string name="permissions_calendar_request">Запрос разрешений для календаря</string>
<string name="permissions_contacts">Разрешения для контактов</string>
<string name="permissions_contacts_details">Для синхронизации CardDAV адресных книг с Вашими локальными контактами DAVdroid должен иметь доступ к Вашим контактам.</string>
<string name="permissions_contacts_request">Запрос разрешений для контактов</string>
<string name="permissions_opentasks">Разрешения OpenTasks</string>
<string name="permissions_opentasks_details">Для синхронизации CalDAV задач с Вашими локальными списками задач DAVdroid должен иметь доступ к OpenTasks.</string>
<string name="permissions_opentasks_request">Запрос разрешений для OpenTasks</string>
<!--AddAccountActivity-->
<string name="login_title">Добавить аккаунт</string>
<string name="login_type_email">Вход по адресу электронной почты</string>
<string name="login_email_address">Адрес электронной почты</string>
<string name="login_email_address_error">Требуется действующий адрес электронной почты</string>
<string name="login_password">Пароль</string>
<string name="login_password_required">Требуется пароль</string>
<string name="login_type_url">Вход через URL и имя пользователя</string>
<string name="login_url_must_be_http_or_https">URL должен начинаться с http(s)://</string>
<string name="login_url_host_name_required">Требуется имя хоста</string>
<string name="login_user_name">Имя</string>
<string name="login_user_name_required">Требуется имя</string>
<string name="login_base_url">Базовый URL</string>
<string name="login_login">Логин</string>
<string name="login_back">Назад</string>
<string name="login_create_account">Создать аккаунт</string>
<string name="login_account_name">Имя аккаунта</string>
<string name="login_account_name_info">Используйте Ваш адрес адрес электронной почты в качестве имени аккаунта, так как Android будет использовать имя аккаунта в поле ORGANIZER для событий, которые Вы создаёте. Вы не можете иметь два аккаунта с одинаковыми именами.</string>
<string name="login_account_contact_group_method">Метод группировки контактов:</string>
<string name="login_account_name_required">Требуется имя аккаунта</string>
<string name="login_account_not_created">Аккаунт не может быть создан</string>
<string name="login_configuration_detection">Обнаружение конфигурации</string>
<string name="login_querying_server">Пожалуйста, подождите, выполняется запрос к серверу...</string>
<string name="login_no_caldav_carddav">Не найдены CalDAV или CardDAV сервисы.</string>
<string name="login_view_logs">Просмотр логов</string>
<!--AccountSettingsActivity-->
<string name="settings_title">Настройки: %s</string>
<string name="settings_authentication">Аутентификация</string>
<string name="settings_username">Имя</string>
<string name="settings_enter_username">Введите имя пользователя:</string>
<string name="settings_password">Пароль</string>
<string name="settings_password_summary">Обновить пароль в соответствии с вашим сервером.</string>
<string name="settings_enter_password">Введите свой пароль:</string>
<string name="settings_sync">Синхронизация</string>
<string name="settings_sync_interval_contacts">Интервал синхронизации контактов</string>
<string name="settings_sync_summary_manually">Вручную</string>
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Каждые %d минут и немедленно при локальных изменениях</string>
<string name="settings_sync_interval_calendars">Интервал синхронизации календарей</string>
<string name="settings_sync_interval_tasks">Интервал синхронизации задач</string>
<string-array name="settings_sync_interval_seconds">
<item>-1</item>
<item>300</item>
<item>600</item>
<item>900</item>
<item>3600</item>
<item>7200</item>
<item>14400</item>
<item>86400</item>
</string-array>
<string-array name="settings_sync_interval_names">
<item>Вручную</item>
<item>Каждые 5 минут</item>
<item>Каждые 10 минут</item>
<item>Каждые 15 минут</item>
<item>Каждый час</item>
<item>Каждые 2 часа</item>
<item>Каждые 4 часа</item>
<item>Раз в сутки</item>
</string-array>
<string name="settings_sync_wifi_only">Синхронизировать только через WiFi</string>
<string name="settings_sync_wifi_only_on">Разрешить синхронизацию только через WiFi</string>
<string name="settings_sync_wifi_only_off">Не учитывать тип соединения</string>
<string name="settings_sync_wifi_only_ssid">Разрешённые WiFi SSID</string>
<string name="settings_sync_wifi_only_ssid_on">Синхронизировать только через %s</string>
<string name="settings_sync_wifi_only_ssid_off">Могут использоваться все WiFi подключения</string>
<string name="settings_sync_wifi_only_ssid_message">Укажите имя WiFi сети (SSID), чтобы разрешить синхронизацию только через эту сеть. Или оставьте пустым для синхронизации через любые WiFi соединения.</string>
<string name="settings_carddav">CardDAV</string>
<string name="settings_contact_group_method">Метод группировки контактов</string>
<string-array name="settings_contact_group_method_entries">
<item>Группы как отдельные vCards</item>
<item>Группы как категории внутри контакта</item>
</string-array>
<string name="settings_caldav">CalDAV</string>
<string name="settings_sync_time_range_past">Интервал синхронизации</string>
<string name="settings_sync_time_range_past_none">Все события будут синхронизированы</string>
<plurals name="settings_sync_time_range_past_days">
<item quantity="one">События старше одного дня будут игнорироваться</item>
<item quantity="few">События старше %d дней будут игнорироваться</item>
<item quantity="many">События старше %d дней будут игнорироваться</item>
<item quantity="other">События старше %d дней будут игнорироваться</item>
</plurals>
<string name="settings_sync_time_range_past_message">События старше указанного количества дней будут игнорироваться (может быть 0). Оставьте пустым для синхронизации всех событий.</string>
<string name="settings_manage_calendar_colors">Управление цветами календаря</string>
<string name="settings_manage_calendar_colors_on">Цвета календаря управляются DAVdroid</string>
<string name="settings_manage_calendar_colors_off">Цвета календаря не управляются DAVdroid</string>
<!--collection management-->
<string name="create_addressbook">Создать адресную книгу</string>
<string name="create_addressbook_display_name_hint">Моя Адресная книга</string>
<string name="create_calendar">Создать CalDAV коллекцию</string>
<string name="create_calendar_display_name_hint">Мой Календарь</string>
<string name="create_calendar_time_zone">Часовой пояс:</string>
<string name="create_calendar_type">Тип коллекции:</string>
<string name="create_calendar_type_only_events">Календарь (только события)</string>
<string name="create_calendar_type_only_tasks">Список задач (только задачи)</string>
<string name="create_calendar_type_events_and_tasks">Совмещённый (события и задачи)</string>
<string name="create_collection_color">Установить цвет коллекции</string>
<string name="create_collection_creating">Создание коллекции</string>
<string name="create_collection_display_name">Отображаемое имя (название) этой коллекции:</string>
<string name="create_collection_display_name_required">Требуется название</string>
<string name="create_collection_description">Описание (необязательно):</string>
<string name="create_collection_home_set">Главная папка:</string>
<string name="create_collection_create">Создать</string>
<string name="delete_collection">Удалить коллекцию</string>
<string name="delete_collection_confirm_title">Вы уверены?</string>
<string name="delete_collection_confirm_warning">Коллекция (%s) и все её данные будут удалены с сервера.</string>
<string name="delete_collection_deleting_collection">Удаление коллекции</string>
<!--ExceptionInfoFragment-->
<string name="exception">Произошла ошибка.</string>
<string name="exception_httpexception">Произошла ошибка HTTP</string>
<string name="exception_ioexception">Произошла ошибка ввода/вывода.</string>
<string name="exception_show_details">Подробнее</string>
<!--sync errors and DebugInfoActivity-->
<string name="debug_info_title">Отладочная информация</string>
<string name="sync_error_permissions">Разрешения DAVdroid</string>
<string name="sync_error_permissions_text">Требуются дополнительные разрешения</string>
<string name="sync_error_calendar">Ошибка при синхронизации Календаря (%s)</string>
<string name="sync_error_contacts">Ошибка при синхронизации Контактов (%s)</string>
<string name="sync_error_tasks">Ошибка при синхронизации Задачи (%s)</string>
<string name="sync_error">Ошибка %s</string>
<string name="sync_error_http_dav">Ошибка сервера %s</string>
<string name="sync_error_local_storage">Ошибка базы данных %s</string>
<string-array name="sync_error_phases">
<item>подготовка синхронизации</item>
<item>запрос поддерживаемых возможностей</item>
<item>обработка локально удалённых записей</item>
<item>подготовка созданных/изменённых записей</item>
<item>загрузка созданных/изменённых записей</item>
<item>проверка статуса синхронизации</item>
<item>просмотр локальных записей</item>
<item>просмотр серверных записей</item>
<item>сравнение локальных/серверных записей</item>
<item>загрузка серверных записей</item>
<item>пост-обработка</item>
<item>сохранение статуса синхронизации</item>
</string-array>
<string name="sync_error_unauthorized">Имя пользователя/пароль неверные</string>
<!--cert4android-->
<string name="certificate_notification_connection_security">DAVdroid: Безопасность подключения</string>
<string name="trust_certificate_unknown_certificate_found">DAVdroid обнаружил неизвестный сертификат. Вы хотите доверять ему?</string>
</resources>
@@ -1,18 +0,0 @@
<!--
~ Copyright © 2013 2016 Ricki Hirner (bitfire web engineering).
~ All rights reserved. This program and the accompanying materials
~ are made available under the terms of the GNU Public License v3.0
~ which accompanies this distribution, and is available at
~ http://www.gnu.org/licenses/gpl.html
-->
<resources>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>
@@ -1,247 +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 通讯录</string>
<string name="address_books_authority_title">通讯录</string>
<string name="help">帮助</string>
<string name="manage_accounts">管理账户</string>
<string name="please_wait">请稍等...</string>
<string name="send">发送</string>
<string name="homepage_url">https://davdroid.bitfire.at/?pk_campaign=davdroid-app</string>
<!--startup dialogs-->
<string name="startup_battery_optimization">电池优化</string>
<string name="startup_battery_optimization_message">系统可能会在几天后减少或停用 DAVdroid 同步。为了避免这一情况,请禁用对 DAVdroid 的电池优化。</string>
<string name="startup_battery_optimization_disable">禁用电池优化</string>
<string name="startup_dont_show_again">不再显示</string>
<string name="startup_development_version_give_feedback">反馈</string>
<string name="startup_donate">开源信息</string>
<string name="startup_donate_message">欢迎使用 DAVdroid,这是一款开源软件 (GPLv3)。开发 DAVdroid 的工作花费了数千小时,请您考虑捐助我们。</string>
<string name="startup_donate_now">显示捐助页面</string>
<string name="startup_donate_later">稍后提示</string>
<string name="startup_google_play_accounts_removed">Play 商店 DRM 问题提醒</string>
<string name="startup_google_play_accounts_removed_message">在某些情况下,Play 商店的 DRM 可能会导致所有 DAVdroid 账户在设备重启或升级 DAVdroid 后消失。如果你遇到了该问题,请从 Play 商店安装“DAVdroid JB Workaround”,否则请不要安装修复程序。</string>
<string name="startup_google_play_accounts_removed_more_info">更多信息</string>
<string name="startup_opentasks_not_installed">OpenTasks 未安装</string>
<string name="startup_opentasks_not_installed_message">未安装 OpenTasks 应用,故 DAVdroid 无法同步任务列表。</string>
<string name="startup_opentasks_reinstall_davdroid">安装 OpenTasks 后,由于 Android 的限制,请重新安装 DAVdroid 并重新创建账户。</string>
<string name="startup_opentasks_not_installed_install">安装 OpenTasks</string>
<!--AboutActivity-->
<string name="about_license_terms">许可协议</string>
<string name="about_license_info_no_warranty">本程序不附带任何担保。这是一款自由软件,你可以有条件地传播它。</string>
<!--global settings-->
<string name="logging_davdroid_file_logging">DAVdroid 文件日志</string>
<string name="logging_to_external_storage">记录日志到外部存储 %s</string>
<string name="logging_to_external_storage_warning">请尽快删除日志!</string>
<string name="logging_couldnt_create_file">无法创建外部日志文件 %s</string>
<string name="logging_no_external_storage">找不到外部存储</string>
<!--AccountsActivity-->
<string name="navigation_drawer_open">打开导航抽屉</string>
<string name="navigation_drawer_close">关闭导航抽屉</string>
<string name="navigation_drawer_subtitle">CalDAV/CardDAV 同步器</string>
<string name="navigation_drawer_about">关于 / 许可</string>
<string name="navigation_drawer_settings">设置</string>
<string name="navigation_drawer_news_updates">最新消息</string>
<string name="navigation_drawer_external_links">外部链接</string>
<string name="navigation_drawer_website">应用网站</string>
<string name="navigation_drawer_faq">常见问题</string>
<string name="navigation_drawer_faq_url">https://davdroid.bitfire.at/faq/?pk_campaign=davdroid-app</string>
<string name="navigation_drawer_donate">捐助</string>
<string name="account_list_empty">欢迎使用 DAVdroid\n\n现在你可以增加 CalDAV/CardDAV 账户。</string>
<string name="accounts_global_sync_disabled">系统全局自动同步已禁用</string>
<string name="accounts_global_sync_enable">启用</string>
<!--DavService-->
<string name="dav_service_refresh_failed">服务配置检测失败</string>
<string name="dav_service_refresh_couldnt_refresh">无法刷新集合列表</string>
<!--AppSettingsActivity-->
<string name="app_settings">设置</string>
<string name="app_settings_user_interface">用户界面</string>
<string name="app_settings_reset_hints">重设提示</string>
<string name="app_settings_reset_hints_summary">重新显示之前忽略过的提示</string>
<string name="app_settings_reset_hints_success">所有提示将会再次显示</string>
<string name="app_settings_connection">连接</string>
<string name="app_settings_override_proxy">覆盖代理设置</string>
<string name="app_settings_override_proxy_on">使用自定义代理设置</string>
<string name="app_settings_override_proxy_off">使用系统默认代理设置</string>
<string name="app_settings_override_proxy_host">HTTP 代理主机名</string>
<string name="app_settings_override_proxy_port">HTTP 代理端口</string>
<string name="app_settings_security">安全</string>
<string name="app_settings_distrust_system_certs">不信任系统证书</string>
<string name="app_settings_distrust_system_certs_on">系统和用户增加的发布者不会被信任</string>
<string name="app_settings_distrust_system_certs_off">系统和用户增加的发布者会被信任(推荐)</string>
<string name="app_settings_reset_certificates">重设证书信任状态</string>
<string name="app_settings_reset_certificates_summary">重设所有自定义证书的信任状态</string>
<string name="app_settings_reset_certificates_success">所有自定义证书已清除</string>
<string name="app_settings_debug">调试</string>
<string name="app_settings_log_to_external_storage">外部文件日志</string>
<string name="app_settings_log_to_external_storage_on">记录日志到外部存储(如果可用)</string>
<string name="app_settings_log_to_external_storage_off">外部文件日志已禁用</string>
<string name="app_settings_show_debug_info">显示调试信息</string>
<string name="app_settings_show_debug_info_details">查看软件和配置信息</string>
<!--AccountActivity-->
<string name="account_synchronize_now"> 立即同步</string>
<string name="account_synchronizing_now">正在同步</string>
<string name="account_settings">账户设置</string>
<string name="account_rename">重命名账户</string>
<string name="account_rename_new_name">重命名后,未上传的本地修改会被撤销,您需要重新执行同步。新账户名:</string>
<string name="account_rename_rename">重命名</string>
<string name="account_delete">删除账户</string>
<string name="account_delete_confirmation_title">真的要删除账户吗?</string>
<string name="account_delete_confirmation_text">所有通讯录、日历和任务列表的本机存储将被删除。</string>
<string name="account_refresh_address_book_list">刷新通讯录列表</string>
<string name="account_create_new_address_book">创建通讯录</string>
<string name="account_refresh_calendar_list">刷新日历列表</string>
<string name="account_create_new_calendar">创建日历</string>
<!--PermissionsActivity-->
<string name="permissions_title">DAVdroid 权限</string>
<string name="permissions_calendar">日历权限</string>
<string name="permissions_calendar_details">要把 CalDAV 事件与本地日历同步,DAVdroid 需要日历权限。</string>
<string name="permissions_calendar_request">请求日历权限</string>
<string name="permissions_contacts">通讯录权限</string>
<string name="permissions_contacts_details">要把 CardDAV 通讯录与本地通讯录同步,DAVdroid 需要通讯录权限。</string>
<string name="permissions_contacts_request">请求通讯录权限</string>
<string name="permissions_opentasks">OpenTasks 权限</string>
<string name="permissions_opentasks_details">要把 CalDAV 任务与本地任务列表同步,DAVdroid 需要访问 OpenTasks。</string>
<string name="permissions_opentasks_request">请求 OpenTasks 权限</string>
<!--AddAccountActivity-->
<string name="login_help_url">https://davdroid.bitfire.at/configuration/?pk_campaign=davdroid-app</string>
<string name="login_title">增加账户</string>
<string name="login_type_email">使用邮箱地址登录</string>
<string name="login_email_address">Email 地址</string>
<string name="login_email_address_error">请输入有效 Email 地址</string>
<string name="login_password">密码</string>
<string name="login_password_required">请输入密码</string>
<string name="login_type_url">使用 URL 和用户名登录</string>
<string name="login_url_must_be_http_or_https">URL 必须以 http(s):// 为开头</string>
<string name="login_url_host_name_required">请输入主机名</string>
<string name="login_user_name">用户名</string>
<string name="login_user_name_required">请输入用户名</string>
<string name="login_base_url">根地址</string>
<string name="login_login">登录</string>
<string name="login_back">返回</string>
<string name="login_create_account">创建账户</string>
<string name="login_account_name">账户显示名</string>
<string name="login_account_name_info">请使用你的邮箱地址作为帐户名,因为 Android 会将你创建的日历事件的创建者项设置为帐户名。你不能拥有多个帐户名相同的账户。</string>
<string name="login_account_contact_group_method">联系人分组方式</string>
<string name="login_account_name_required">请输入账户名</string>
<string name="login_account_not_created">账户无法创建</string>
<string name="login_configuration_detection">正在配置</string>
<string name="login_querying_server">正在与服务器通信,请稍等...</string>
<string name="login_no_caldav_carddav">找不到 CalDAV 或 CardDAV 服务。</string>
<string name="login_view_logs">查看日志</string>
<!--AccountSettingsActivity-->
<string name="settings_title">设置:%s</string>
<string name="settings_authentication">认证</string>
<string name="settings_username">用户名</string>
<string name="settings_enter_username">输入用户名</string>
<string name="settings_password">密码</string>
<string name="settings_password_summary">修改服务器密码</string>
<string name="settings_enter_password">输入密码</string>
<string name="settings_sync">同步</string>
<string name="settings_sync_interval_contacts">通讯录自动同步间隔</string>
<string name="settings_sync_summary_manually">手动同步</string>
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">每 %d 分钟或本地修改后</string>
<string name="settings_sync_interval_calendars">日历自动同步间隔</string>
<string name="settings_sync_interval_tasks">任务自动同步间隔</string>
<string-array name="settings_sync_interval_seconds">
<item>-1</item>
<item>300</item>
<item>600</item>
<item>900</item>
<item>3600</item>
<item>7200</item>
<item>14400</item>
<item>86400</item>
</string-array>
<string-array name="settings_sync_interval_names">
<item>手动同步</item>
<item>每 5 分钟</item>
<item>每 10 分钟</item>
<item>每 15 分钟</item>
<item>每 1 小时</item>
<item>每 2 小时</item>
<item>每 4 小时</item>
<item>每 24 小时</item>
</string-array>
<string name="settings_sync_wifi_only">只在 WiFi 下同步</string>
<string name="settings_sync_wifi_only_on">同步只在 WiFi 连接下进行</string>
<string name="settings_sync_wifi_only_off">同步不受数据连接类型限制</string>
<string name="settings_sync_wifi_only_ssid">WiFi SSID 限制</string>
<string name="settings_sync_wifi_only_ssid_on">同步只在 %s 网络下进行</string>
<string name="settings_sync_wifi_only_ssid_off">任何 WiFi 网络下均会同步</string>
<string name="settings_sync_wifi_only_ssid_message">输入 WiFi 网络的名称 (SSID) ,即可限制同步只在此网络下进行。留空则不限制。</string>
<string name="settings_carddav">CardDAV</string>
<string name="settings_contact_group_method">联系人分组方式</string>
<string-array name="settings_contact_group_method_values">
<item>GROUP_VCARDS</item>
<item>CATEGORIES</item>
</string-array>
<string-array name="settings_contact_group_method_entries">
<item>分为不同的 VCards</item>
<item>每个联系人的分类</item>
</string-array>
<string name="settings_caldav">CalDAV</string>
<string name="settings_sync_time_range_past">旧日程时间限制</string>
<string name="settings_sync_time_range_past_none">同步所有日程</string>
<plurals name="settings_sync_time_range_past_days">
<item quantity="other">%d 天前的日程不会被同步</item>
</plurals>
<string name="settings_sync_time_range_past_message">超过这个数字的天数的旧日程将会被忽略(可以为 0)。留空则同步所有日程。</string>
<string name="settings_manage_calendar_colors">管理日历颜色</string>
<string name="settings_manage_calendar_colors_on">日历颜色由 DAVdroid 设置</string>
<string name="settings_manage_calendar_colors_off">日历颜色不由 DAVdroid 设置</string>
<!--collection management-->
<string name="create_addressbook">创建通讯录</string>
<string name="create_addressbook_display_name_hint">我的通讯录</string>
<string name="create_calendar">创建 CalDAV 集合</string>
<string name="create_calendar_display_name_hint">我的日历</string>
<string name="create_calendar_time_zone">时区</string>
<string name="create_calendar_type">集合类型</string>
<string name="create_calendar_type_only_events">日历(只有日程)</string>
<string name="create_calendar_type_only_tasks">任务列表(只有任务)</string>
<string name="create_calendar_type_events_and_tasks">混合(日程和任务)</string>
<string name="create_collection_color">设置集合颜色</string>
<string name="create_collection_creating">正在创建集合</string>
<string name="create_collection_display_name">集合显示名称(标题)</string>
<string name="create_collection_display_name_required">请输入标题</string>
<string name="create_collection_description">简介(可选)</string>
<string name="create_collection_home_set">主集合</string>
<string name="create_collection_create">创建</string>
<string name="delete_collection">删除集合</string>
<string name="delete_collection_confirm_title">你确定吗?</string>
<string name="delete_collection_confirm_warning">这个集合 %s 及其所有数据将会从服务器删除。</string>
<string name="delete_collection_deleting_collection">正在删除集合</string>
<!--ExceptionInfoFragment-->
<string name="exception">出现错误</string>
<string name="exception_httpexception">出现 HTTP 错误</string>
<string name="exception_ioexception">出现 I/O 错误</string>
<string name="exception_show_details">显示详情</string>
<!--sync errors and DebugInfoActivity-->
<string name="debug_info_title">调试信息</string>
<string name="sync_error_permissions">DAVdroid 权限</string>
<string name="sync_error_permissions_text">需要额外权限</string>
<string name="sync_error_calendar">日历同步失败(%s</string>
<string name="sync_error_contacts">通讯录同步失败(%s</string>
<string name="sync_error_tasks">任务同步失败(%s</string>
<string name="sync_error">%s时错误</string>
<string name="sync_error_http_dav">%s时服务器错误</string>
<string name="sync_error_local_storage">%s时数据库错误</string>
<string-array name="sync_error_phases">
<item>准备同步</item>
<item>请求功能列表</item>
<item>处理本地删除项目</item>
<item>准备创建/修改项目</item>
<item>上传创建/修改项目</item>
<item>检查同步状态</item>
<item>检查本地数据</item>
<item>检查远程数据</item>
<item>比较本地和远程数据</item>
<item>下载远程数据</item>
<item>预处理</item>
<item>保存同步状态</item>
</string-array>
<string name="sync_error_unauthorized">用户名或密码错误</string>
<!--cert4android-->
<string name="certificate_notification_connection_security">DAVdroid: 连接安全性</string>
<string name="trust_certificate_unknown_certificate_found">DAVdroid 遇到了未知证书。你是否要信任该证书?</string>
</resources>
-7
View File
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="at.bitfire.davdroid"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />
</manifest>
@@ -1,54 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright © 2013 2016 Ricki Hirner (bitfire web engineering).
~ All rights reserved. This program and the accompanying materials
~ are made available under the terms of the GNU Public License v3.0
~ which accompanies this distribution, and is available at
~ http://www.gnu.org/licenses/gpl.html
-->
<menu xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/nav_about"
android:icon="@drawable/ic_info_dark"
android:title="@string/navigation_drawer_about"/>
<item
android:id="@+id/nav_app_settings"
android:icon="@drawable/ic_settings_action"
android:title="@string/navigation_drawer_settings"/>
<item android:title="@string/navigation_drawer_news_updates">
<menu>
<item
android:id="@+id/nav_twitter"
android:icon="@drawable/twitter"
android:title="\@davdroidapp"
tools:ignore="HardcodedText"/>
</menu>
</item>
<item android:title="@string/navigation_drawer_external_links">
<menu>
<item
android:id="@+id/nav_website"
android:icon="@drawable/ic_home_dark"
android:title="@string/navigation_drawer_website"/>
<item
android:id="@+id/nav_faq"
android:icon="@drawable/ic_help_action"
android:title="@string/navigation_drawer_faq"/>
<item
android:id="@+id/nav_forums"
android:icon="@drawable/ic_forum_dark"
android:title="@string/navigation_drawer_forums"/>
<item
android:id="@+id/nav_donate"
android:icon="@drawable/ic_attach_money_dark"
android:title="@string/navigation_drawer_donate"
android:visible="false"/>
</menu>
</item>
</menu>
+132 -108
View File
@@ -1,11 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2013 2015 Ricki Hirner (bitfire web engineering).
~ All rights reserved. This program and the accompanying materials
~ are made available under the terms of the GNU Public License v3.0
~ which accompanies this distribution, and is available at
~ http://www.gnu.org/licenses/gpl.html
-->
<manifest package="at.bitfire.davdroid"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
@@ -14,10 +7,12 @@
<!-- normal permissions -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.READ_SYNC_STATS"/>
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<!-- account management permissions not required for own accounts since API level 22 -->
@@ -25,13 +20,6 @@
<uses-permission android:name="android.permission.GET_ACCOUNTS" android:maxSdkVersion="22"/>
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" android:maxSdkVersion="22"/>
<!--
for writing external log files; permission only required for SDK <= 18 because since then,
writing to app-private directory doesn't require extra permissions
-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="18"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18"/>
<!-- other permissions -->
<!-- android.permission-group.CONTACTS -->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
@@ -40,43 +28,112 @@
<uses-permission android:name="android.permission.READ_CALENDAR"/>
<uses-permission android:name="android.permission.WRITE_CALENDAR"/>
<!-- android.permission-group.LOCATION -->
<!-- getting the WiFi name (for "sync in Wifi only") requires
- coarse location (Android 8.1)
- fine location (Android 10) -->
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!-- required since Android 10 to get the WiFi name while in background (= while syncing) -->
<uses-permission-sdk-23 android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<!-- ical4android declares task access permissions -->
<application
android:name=".App"
android:allowBackup="true"
android:fullBackupContent="false"
android:allowBackup="false"
android:networkSecurityConfig="@xml/network_security_config"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:resizeableActivity="true"
tools:ignore="UnusedAttribute">
<receiver
android:name=".model.Settings$ReinitSettingsReceiver"
android:exported="false"
android:process=":sync">
<service android:name=".DavService"/>
<service android:name=".ForegroundService"/>
<activity android:name=".ui.intro.IntroActivity" android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".ui.AccountsActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="at.bitfire.davdroid.REINIT_SETTINGS"/>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</receiver>
<receiver
android:name=".AccountSettings$AppUpdatedReceiver"
</activity>
<activity
android:name=".ui.AboutActivity"
android:label="@string/navigation_drawer_about"
android:theme="@style/AppTheme.NoActionBar"
android:parentActivityName=".ui.AccountsActivity"/>
<activity
android:name=".ui.AppSettingsActivity"
android:label="@string/app_settings"
android:parentActivityName=".ui.AccountsActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package" android:path="at.bitfire.davdroid" />
<action android:name="android.intent.action.APPLICATION_PREFERENCES"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
</activity>
<activity
android:name=".ui.DebugInfoActivity"
android:parentActivityName=".ui.AppSettingsActivity"
android:exported="true"
android:label="@string/debug_info_title">
<intent-filter>
<action android:name="android.intent.action.BUG_REPORT"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".ui.PermissionsActivity"
android:label="@string/app_settings_security_app_permissions"
android:parentActivityName=".ui.AppSettingsActivity" />
<activity
android:name=".ui.TasksActivity"
android:label="@string/intro_tasks_title"
android:parentActivityName=".ui.AppSettingsActivity" />
<!-- account type "DAVdroid" -->
<activity
android:name=".ui.setup.LoginActivity"
android:label="@string/login_title"
android:parentActivityName=".ui.AccountsActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
<activity
android:name=".ui.account.AccountActivity"
android:parentActivityName=".ui.AccountsActivity"
android:theme="@style/AppTheme.NoActionBar"/>
<activity
android:name=".ui.account.CreateAddressBookActivity"
android:label="@string/create_addressbook"
android:parentActivityName=".ui.account.AccountActivity" />
<activity
android:name=".ui.account.CreateCalendarActivity"
android:label="@string/create_calendar"
android:parentActivityName=".ui.account.AccountActivity" />
<activity
android:name=".ui.account.SettingsActivity"
android:parentActivityName=".ui.account.AccountActivity" />
<activity
android:name=".ui.account.WifiPermissionsActivity"
android:label="@string/wifi_permissions_label"
android:parentActivityName=".ui.account.SettingsActivity" />
<!-- account type "DAVx⁵" -->
<service
android:name=".syncadapter.AccountAuthenticatorService"
android:exported="false">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/account_authenticator"/>
@@ -84,35 +141,41 @@
<service
android:name=".syncadapter.CalendarsSyncAdapterService"
android:exported="true"
android:process=":sync"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/sync_calendars"/>
</service>
<service
android:name=".syncadapter.TasksSyncAdapterService"
android:name=".syncadapter.OpenTasksSyncAdapterService"
android:exported="true"
android:process=":sync"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/sync_tasks"/>
android:resource="@xml/sync_opentasks"/>
</service>
<service
android:name=".syncadapter.TasksOrgSyncAdapterService"
android:exported="true"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/sync_tasks_org"/>
</service>
<!-- account type "DAVdroid Address book" -->
<!-- account type "DAVx⁵ Address book" -->
<service
android:name=".syncadapter.NullAuthenticatorService"
android:exported="false">
android:exported="true"> <!-- Since Android 11, this must be true so that Google Contacts shows the address book accounts -->
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
@@ -130,7 +193,6 @@
<service
android:name=".syncadapter.AddressBooksSyncAdapterService"
android:exported="true"
android:process=":sync"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.content.SyncAdapter"/>
@@ -143,7 +205,6 @@
<service
android:name=".syncadapter.ContactsSyncAdapterService"
android:exported="true"
android:process=":sync"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.content.SyncAdapter"/>
@@ -157,85 +218,48 @@
android:resource="@xml/contacts"/>
</service>
<service
android:name=".DavService"
android:enabled="true">
</service>
<receiver android:name=".AccountsChangedReceiver">
<receiver android:name=".BootCompletedReceiver">
<intent-filter>
<action android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED"/>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".PackageChangedReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED"/>
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED"/>
<data android:scheme="package"/>
</intent-filter>
</receiver>
<activity
android:name=".ui.AccountsActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".ui.AboutActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar"
android:parentActivityName=".ui.AccountsActivity"/>
<activity
android:name=".ui.AppSettingsActivity"
android:label="@string/app_settings"
android:parentActivityName=".ui.AccountsActivity"/>
<activity android:name=".ui.PermissionsActivity"
android:label="@string/permissions_title"
android:parentActivityName=".ui.AccountsActivity"/>
<activity
android:name=".ui.setup.LoginActivity"
android:label="@string/login_title"
android:parentActivityName=".ui.AccountsActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
<activity
android:name=".ui.AccountActivity"
android:parentActivityName=".ui.AccountsActivity">
</activity>
<activity android:name=".ui.AccountSettingsActivity"/>
<activity android:name=".ui.CreateAddressBookActivity"
android:label="@string/create_addressbook"/>
<activity android:name=".ui.CreateCalendarActivity"
android:label="@string/create_calendar"/>
<activity
android:name=".ui.DebugInfoActivity"
android:exported="true"
android:label="@string/debug_info_title">
</activity>
<!-- provider to share debug info/logs -->
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="@string/authority_log_provider"
android:name="androidx.core.content.FileProvider"
android:authorities="@string/authority_debug_provider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/log_paths" />
android:resource="@xml/debug_paths" />
</provider>
</application>
<!-- package visiblity which apps do we need to see? -->
<queries>
<!-- task providers -->
<package android:name="org.dmfs.tasks" />
<package android:name="org.tasks" />
<!-- ICSx5 for Webcal feeds -->
<package android:name="at.bitfire.icsdroid"/>
<!-- apps that interact with contact, calendar, task data (for debug info) -->
<intent>
<action android:name="*" />
<data android:scheme="content" android:host="com.android.contacts" />
</intent>
<intent>
<action android:name="*" />
<data android:scheme="content" android:host="com.android.calendar" />
</intent>
<!-- Custom Tabs support (e.g. Nextcloud Login Flow) -->
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
</queries>
</manifest>
-153
View File
@@ -1,153 +0,0 @@
<h3>Apache License, Version 2.0, January 2004</h3>
<p><a href="http://www.apache.org/licenses/">http://www.apache.org/licenses/</a> </p>
<p>TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION</p>
<p><strong><a name="definitions">1. Definitions</a></strong>.</p>
<p>"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.</p>
<p>"Licensor" shall mean the copyright owner or entity authorized by the
copyright owner that is granting the License.</p>
<p>"Legal Entity" shall mean the union of the acting entity and all other
entities that control, are controlled by, or are under common control with
that entity. For the purposes of this definition, "control" means (i) the
power, direct or indirect, to cause the direction or management of such
entity, whether by contract or otherwise, or (ii) ownership of fifty
percent (50%) or more of the outstanding shares, or (iii) beneficial
ownership of such entity.</p>
<p>"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.</p>
<p>"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation source,
and configuration files.</p>
<p>"Object" form shall mean any form resulting from mechanical transformation
or translation of a Source form, including but not limited to compiled
object code, generated documentation, and conversions to other media types.</p>
<p>"Work" shall mean the work of authorship, whether in Source or Object form,
made available under the License, as indicated by a copyright notice that
is included in or attached to the work (an example is provided in the
Appendix below).</p>
<p>"Derivative Works" shall mean any work, whether in Source or Object form,
that is based on (or derived from) the Work and for which the editorial
revisions, annotations, elaborations, or other modifications represent, as
a whole, an original work of authorship. For the purposes of this License,
Derivative Works shall not include works that remain separable from, or
merely link (or bind by name) to the interfaces of, the Work and Derivative
Works thereof.</p>
<p>"Contribution" shall mean any work of authorship, including the original
version of the Work and any modifications or additions to that Work or
Derivative Works thereof, that is intentionally submitted to Licensor for
inclusion in the Work by the copyright owner or by an individual or Legal
Entity authorized to submit on behalf of the copyright owner. For the
purposes of this definition, "submitted" means any form of electronic,
verbal, or written communication sent to the Licensor or its
representatives, including but not limited to communication on electronic
mailing lists, source code control systems, and issue tracking systems that
are managed by, or on behalf of, the Licensor for the purpose of discussing
and improving the Work, but excluding communication that is conspicuously
marked or otherwise designated in writing by the copyright owner as "Not a
Contribution."</p>
<p>"Contributor" shall mean Licensor and any individual or Legal Entity on
behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.</p>
<p><strong><a name="copyright">2. Grant of Copyright License</a></strong>. Subject to the
terms and conditions of this License, each Contributor hereby grants to You
a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of, publicly
display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.</p>
<p><strong><a name="patent">3. Grant of Patent License</a></strong>. Subject to the terms
and conditions of this License, each Contributor hereby grants to You a
perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made, use,
offer to sell, sell, import, and otherwise transfer the Work, where such
license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by
combination of their Contribution(s) with the Work to which such
Contribution(s) was submitted. If You institute patent litigation against
any entity (including a cross-claim or counterclaim in a lawsuit) alleging
that the Work or a Contribution incorporated within the Work constitutes
direct or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate as of the
date such litigation is filed.</p>
<p><strong><a name="redistribution">4. Redistribution</a></strong>. You may reproduce and
distribute copies of the Work or Derivative Works thereof in any medium,
with or without modifications, and in Source or Object form, provided that
You meet the following conditions:</p>
<p>a. You must give any other recipients of the Work or Derivative Works a
copy of this License; and</p>
<p>b. You must cause any modified files to carry prominent notices stating
that You changed the files; and</p>
<p>c. You must retain, in the Source form of any Derivative Works that You
distribute, all copyright, patent, trademark, and attribution notices from
the Source form of the Work, excluding those notices that do not pertain to
any part of the Derivative Works; and</p>
<p>d. If the Work includes a "NOTICE" text file as part of its distribution,
then any Derivative Works that You distribute must include a readable copy
of the attribution notices contained within such NOTICE file, excluding
those notices that do not pertain to any part of the Derivative Works, in
at least one of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or documentation,
if provided along with the Derivative Works; or, within a display generated
by the Derivative Works, if and wherever such third-party notices normally
appear. The contents of the NOTICE file are for informational purposes only
and do not modify the License. You may add Your own attribution notices
within Derivative Works that You distribute, alongside or as an addendum to
the NOTICE text from the Work, provided that such additional attribution
notices cannot be construed as modifying the License.
<br/>
<br/>
You may add Your own copyright statement to Your modifications and may
provide additional or different license terms and conditions for use,
reproduction, or distribution of Your modifications, or for any such
Derivative Works as a whole, provided Your use, reproduction, and
distribution of the Work otherwise complies with the conditions stated in
this License.
</p>
<p><strong><a name="contributions">5. Submission of Contributions</a></strong>. Unless You
explicitly state otherwise, any Contribution intentionally submitted for
inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the
terms of any separate license agreement you may have executed with Licensor
regarding such Contributions.</p>
<p><strong><a name="trademarks">6. Trademarks</a></strong>. This License does not grant
permission to use the trade names, trademarks, service marks, or product
names of the Licensor, except as required for reasonable and customary use
in describing the origin of the Work and reproducing the content of the
NOTICE file.</p>
<p><strong><a name="no-warranty">7. Disclaimer of Warranty</a></strong>. Unless required by
applicable law or agreed to in writing, Licensor provides the Work (and
each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including,
without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You
are solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise
of permissions under this License.</p>
<p><strong><a name="no-liability">8. Limitation of Liability</a></strong>. In no event and
under no legal theory, whether in tort (including negligence), contract, or
otherwise, unless required by applicable law (such as deliberate and
grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a result
of this License or out of the use or inability to use the Work (including
but not limited to damages for loss of goodwill, work stoppage, computer
failure or malfunction, or any and all other commercial damages or losses),
even if such Contributor has been advised of the possibility of such
damages.</p>
<p><strong><a name="additional">9. Accepting Warranty or Additional Liability</a></strong>.
While redistributing the Work or Derivative Works thereof, You may choose
to offer, and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this License.
However, in accepting such obligations, You may act only on Your own behalf
and on Your sole responsibility, not on behalf of any other Contributor,
and only if You agree to indemnify, defend, and hold each Contributor
harmless for any liability incurred by, or claims asserted against, such
Contributor by reason of your accepting any such warranty or additional
liability.</p>
<p>END OF TERMS AND CONDITIONS</p>
-28
View File
@@ -1,28 +0,0 @@
<h3>BSD License (3-clause)</h3>
<p>Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:</p>
<p>o Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.</p>
<p>o Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.</p>
<p>o Neither the name of Ben Fortuna nor the names of any other contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.</p>
<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</p>
-23
View File
@@ -1,23 +0,0 @@
<h3>BSD License</h3>
<p>Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:</p>
<p>1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.</p>
<p>2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.</p>
<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</p>
@@ -2,9 +2,9 @@
<p style="text-align: center;">Version 3, 29 June 2007</p>
<p>Copyright &copy; 2007 Free Software Foundation, Inc.
&lt;<a href="http://fsf.org/">http://fsf.org/</a>&gt;</p><p>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.</p>
&lt;<a href="http://fsf.org/">http://fsf.org/</a>&gt;</p><p>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.</p>
<h3><a name="preamble"></a>Preamble</h3>
@@ -42,16 +42,16 @@ know their rights.</p>
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.</p>
<p>For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
<p>For the developers\' and authors\' protection, the GPL clearly explains
that there is no warranty for this free software. For both users\' and
authors\' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.</p>
<p>Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
protecting users\' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
@@ -77,7 +77,7 @@ modification follow.</p>
<p>&ldquo;Copyright&rdquo; also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.</p>
<p>&ldquo;The Program&rdquo; refers to any copyrightable work licensed under this
License. Each licensee is addressed as &ldquo;you&rdquo;. &ldquo;Licensees&rdquo; and
&ldquo;recipients&rdquo; may be individuals or organizations.</p>
@@ -135,7 +135,7 @@ produce the work, or an object code interpreter used to run it.</p>
<p>The &ldquo;Corresponding Source&rdquo; for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
control those activities. However, it does not include the work\'s
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
@@ -177,7 +177,7 @@ your copyrighted material outside their relationship with you.</p>
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.</p>
<h4><a name="section3"></a>3. Protecting Users' Legal Rights From Anti-Circumvention Law.</h4>
<h4><a name="section3"></a>3. Protecting Users\' Legal Rights From Anti-Circumvention Law.</h4>
<p>No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
@@ -189,13 +189,13 @@ measures.</p>
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
modification of the work as a means of enforcing, against the work\'s
users, your or third parties\' legal rights to forbid circumvention of
technological measures.</p>
<h4><a name="section4"></a>4. Conveying Verbatim Copies.</h4>
<p>You may convey verbatim copies of the Program's source code as you
<p>You may convey verbatim copies of the Program\'s source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
@@ -240,7 +240,7 @@ works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
&ldquo;aggregate&rdquo; if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
used to limit the access or legal rights of the compilation\'s users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.</p>
@@ -462,7 +462,7 @@ organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
licenses to the work the party\'s predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.</p>
@@ -479,9 +479,9 @@ sale, or importing the Program or any portion of it.</p>
<p>A &ldquo;contributor&rdquo; is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's &ldquo;contributor version&rdquo;.</p>
work thus licensed is called the contributor\'s &ldquo;contributor version&rdquo;.</p>
<p>A contributor's &ldquo;essential patent claims&rdquo; are all patent claims
<p>A contributor\'s &ldquo;essential patent claims&rdquo; are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
@@ -492,7 +492,7 @@ patent sublicenses in a manner consistent with the requirements of
this License.</p>
<p>Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
patent license under the contributor\'s essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.</p>
@@ -513,10 +513,10 @@ patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. &ldquo;Knowingly relying&rdquo; means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
covered work in a country, or your recipient\'s use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.</p>
<p>If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
@@ -544,7 +544,7 @@ or that patent license was granted, prior to 28 March 2007.</p>
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.</p>
<h4><a name="section12"></a>12. No Surrender of Others' Freedom.</h4>
<h4><a name="section12"></a>12. No Surrender of Others\' Freedom.</h4>
<p>If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
@@ -584,7 +584,7 @@ GNU General Public License, you may choose any version ever published
by the Free Software Foundation.</p>
<p>If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
versions of the GNU General Public License can be used, that proxy\'s
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.</p>
-7
View File
@@ -1,7 +0,0 @@
<h3>The MIT License (MIT)</h3>
<p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</p>
<p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>
<p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>
+1
View File
@@ -0,0 +1 @@
{"ar_SA":["abdunnasir"],"bg":["dpa_transifex","ma4ko","stoyan"],"ca":["ettenmoors","hegocr_1709","jordibrus","Kintu","Solatec","zagur"],"cs":["Kryšpin","pavelb","tomas.odehnal"],"da":["knutztar","mjjzf","Tntdruid_","twikedk"],"de":["amandablue","anestiskaci","Atalanttore","corppneq","crit12","hammaschlach","maxkl","nicolas_git","owncube","TheName","Waldmeisda","Wyrrrd","YvanM"],"el":["anestiskaci","diamond_gr","KristinaQejvanaj"],"es":["aluaces","Anoih","Ark74","Elhea","GranPC","jcvielma","oscfdezdz","plaguna","polkhas","xphnx"],"eu":["cockeredradiation","Osoitz","Thadah"],"fa":["ahangarha","amiraliakbari","javad.aboutorabi","joojoojoo","maryambehzi","mtashackori","Numb","taranehsaei"],"fi_FI":["raketti","tseipii"],"fr":["AlainR","alkino2","Amadeen","boutil","callmemagnus","chfo","chrcha","Floflr","grenatrad","jokx","Jorg722","JorisBodin","Llorc","LoiX07","mathieugfortin","Novick","paullbn","Poussinou","RomainTT","Synochy","Thecross","unareil","vincen","YvanM","ÉricB."],"fr_FR":["chrcha","Llorc","Poussinou"],"gl":["aluaces","pikamoku"],"hr":["tomiis"],"hr_HR":["tomiis"],"hu":["infeeeee","jtg","meskobalazs","Roshek"],"it":["Damtux","ed0","FranzMari","lzambarda","malaerba","noccio","nwandy","rickyroo","technezio"],"it_IT":["malaerba"],"ja":["Naofumi","yanorei32"],"nb_NO":["elonus"],"nl":["davtemp","dehart","erikhubers","frankyboy1963","glotzbach","toonvangerwen","XtremeNova"],"pl":["gsz","mg6","oskarjakiela","TheName","TORminator","Valdnet"],"pt":["amalvarenga","wanderlei.huttel"],"pt_BR":["wanderlei.huttel"],"ru":["aigoshin","anm","ashed","astalavister","nick.savin","vaddd"],"sk_SK":["brango67","tiborepcek"],"sl_SI":["MrLaaky","uroszor"],"sr":["daimonion"],"sv":["campbelldavid","Mikaelb"],"szl":["chlodny"],"tr_TR":["ooguz","pultars"],"uk":["androsua","olexn","twixi007"],"uk_UA":["astalavister"],"zh_CN":["anolir","jxj2zzz79pfp9bpo","linuxbckp","mofitt2016","oksjd","phy","spice2wolf"],"zh_TW":["linuxbckp","mofitt2016","phy","waiabsfabuloushk"]}
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

@@ -1,460 +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.accounts.Account
import android.accounts.AccountManager
import android.annotation.SuppressLint
import android.content.*
import android.os.Build
import android.os.Bundle
import android.os.Parcel
import android.os.RemoteException
import android.provider.CalendarContract
import android.provider.ContactsContract
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.model.CollectionInfo
import at.bitfire.davdroid.model.ServiceDB
import at.bitfire.davdroid.model.ServiceDB.*
import at.bitfire.davdroid.model.ServiceDB.Collections
import at.bitfire.davdroid.resource.LocalAddressBook
import at.bitfire.davdroid.resource.LocalCalendar
import at.bitfire.davdroid.resource.LocalTaskList
import at.bitfire.ical4android.AndroidCalendar
import at.bitfire.ical4android.AndroidTaskList
import at.bitfire.ical4android.CalendarStorageException
import at.bitfire.vcard4android.ContactsStorageException
import at.bitfire.vcard4android.GroupMethod
import okhttp3.HttpUrl
import java.util.*
import java.util.logging.Level
class AccountSettings @Throws(InvalidAccountException::class) constructor(
val context: Context,
val account: Account
) {
companion object {
val CURRENT_VERSION = 6
val KEY_SETTINGS_VERSION = "version"
val KEY_USERNAME = "user_name"
val KEY_WIFI_ONLY = "wifi_only" // sync on WiFi only (default: false)
val KEY_WIFI_ONLY_SSID = "wifi_only_ssid" // restrict sync to specific WiFi SSID
/** Time range limitation to the past [in days]
value = null default value (DEFAULT_TIME_RANGE_PAST_DAYS)
< 0 (-1) no limit
>= 0 entries more than n days in the past won't be synchronized
*/
val KEY_TIME_RANGE_PAST_DAYS = "time_range_past_days"
val DEFAULT_TIME_RANGE_PAST_DAYS = 90
/* Whether DAVdroid sets the local calendar color to the value from service DB at every sync
value = null (not existing) true (default)
"0" false */
val KEY_MANAGE_CALENDAR_COLORS = "manage_calendar_colors"
/** Contact group method:
value = null (not existing) groups as separate VCards (default)
"CATEGORIES" groups are per-contact CATEGORIES
*/
val KEY_CONTACT_GROUP_METHOD = "contact_group_method"
@JvmField
val SYNC_INTERVAL_MANUALLY = -1L
@JvmStatic
fun initialUserData(userName: String): Bundle
{
val bundle = Bundle(2)
bundle.putString(KEY_SETTINGS_VERSION, CURRENT_VERSION.toString())
bundle.putString(KEY_USERNAME, userName)
return bundle
}
}
val accountManager: AccountManager = AccountManager.get(context)
init {
synchronized(AccountSettings::class.java) {
val versionStr = accountManager.getUserData(account, KEY_SETTINGS_VERSION) ?: throw InvalidAccountException(account)
var version = 0
try {
version = Integer.parseInt(versionStr)
} catch (e: NumberFormatException) {
}
Logger.log.fine("Account ${account.name} has version $version, current version: $CURRENT_VERSION")
if (version < CURRENT_VERSION)
update(version)
}
}
// authentication settings
fun username(): String? = accountManager.getUserData(account, KEY_USERNAME)
fun username(userName: String) = accountManager.setUserData(account, KEY_USERNAME, userName)
fun password(): String? = accountManager.getPassword(account)
fun password(password: String) = accountManager.setPassword(account, password)
// sync. settings
fun getSyncInterval(authority: String): Long? {
if (ContentResolver.getIsSyncable(account, authority) <= 0)
return null
return if (ContentResolver.getSyncAutomatically(account, authority))
ContentResolver.getPeriodicSyncs(account, authority).firstOrNull()?.period ?: SYNC_INTERVAL_MANUALLY
else
SYNC_INTERVAL_MANUALLY
}
fun setSyncInterval(authority: String, seconds: Long) {
if (seconds == SYNC_INTERVAL_MANUALLY) {
ContentResolver.setSyncAutomatically(account, authority, false)
} else {
ContentResolver.setSyncAutomatically(account, authority, true)
ContentResolver.addPeriodicSync(account, authority, Bundle(), seconds)
}
}
fun getSyncWifiOnly() = accountManager.getUserData(account, KEY_WIFI_ONLY) != null
fun setSyncWiFiOnly(wiFiOnly: Boolean) =
accountManager.setUserData(account, KEY_WIFI_ONLY, if (wiFiOnly) "1" else null)
fun getSyncWifiOnlySSID(): String? = accountManager.getUserData(account, KEY_WIFI_ONLY_SSID)
fun setSyncWifiOnlySSID(ssid: String?) = accountManager.setUserData(account, KEY_WIFI_ONLY_SSID, ssid)
// CalDAV settings
fun getTimeRangePastDays(): Int? {
val strDays = accountManager.getUserData(account, KEY_TIME_RANGE_PAST_DAYS)
if (strDays != null) {
val days = Integer.valueOf(strDays)
return if (days < 0) null else days
} else
return DEFAULT_TIME_RANGE_PAST_DAYS
}
fun setTimeRangePastDays(days: Int?) =
accountManager.setUserData(account, KEY_TIME_RANGE_PAST_DAYS, (days ?: -1).toString())
fun getManageCalendarColors() = accountManager.getUserData(account, KEY_MANAGE_CALENDAR_COLORS) == null
fun setManageCalendarColors(manage: Boolean) =
accountManager.setUserData(account, KEY_MANAGE_CALENDAR_COLORS, if (manage) null else "0")
// CardDAV settings
fun getGroupMethod(): GroupMethod {
BuildConfig.settingContactGroupMethod?.let { return it }
val name = accountManager.getUserData(account, KEY_CONTACT_GROUP_METHOD)
return if (name != null)
GroupMethod.valueOf(name)
else
GroupMethod.GROUP_VCARDS
}
fun setGroupMethod(method: GroupMethod) {
if (BuildConfig.settingContactGroupMethod == null) {
val name = if (method == GroupMethod.GROUP_VCARDS) null else method.name
accountManager.setUserData(account, KEY_CONTACT_GROUP_METHOD, name)
} else if (BuildConfig.settingContactGroupMethod != method)
throw UnsupportedOperationException("Group method setting is read-only")
}
// update from previous account settings
private fun update(baseVersion: Int) {
for (toVersion in baseVersion+1 .. CURRENT_VERSION) {
val fromVersion = toVersion-1
Logger.log.info("Updating account ${account.name} from version $fromVersion to $toVersion")
try {
val updateProc = this::class.java.getDeclaredMethod("update_${fromVersion}_$toVersion")
updateProc.invoke(this)
accountManager.setUserData(account, KEY_SETTINGS_VERSION, toVersion.toString())
} catch (e: Exception) {
Logger.log.log(Level.SEVERE, "Couldn't update account settings", e)
}
}
}
@Suppress("unused")
private fun update_1_2() {
/* - KEY_ADDRESSBOOK_URL ("addressbook_url"),
- KEY_ADDRESSBOOK_CTAG ("addressbook_ctag"),
- KEY_ADDRESSBOOK_VCARD_VERSION ("addressbook_vcard_version") are not used anymore (now stored in ContactsContract.SyncState)
- KEY_LAST_ANDROID_VERSION ("last_android_version") has been added
*/
// move previous address book info to ContactsContract.SyncState
val provider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY) ?:
throw ContactsStorageException("Couldn't access Contacts provider")
try {
val addr = LocalAddressBook(context, account, provider)
// until now, ContactsContract.Settings.UNGROUPED_VISIBLE was not set explicitly
val values = ContentValues()
values.put(ContactsContract.Settings.UNGROUPED_VISIBLE, 1)
addr.updateSettings(values)
val url = accountManager.getUserData(account, "addressbook_url")
if (!url.isNullOrEmpty())
addr.setURL(url)
accountManager.setUserData(account, "addressbook_url", null)
val cTag = accountManager.getUserData (account, "addressbook_ctag")
if (!cTag.isNullOrEmpty())
addr.setCTag(cTag)
accountManager.setUserData(account, "addressbook_ctag", null)
} finally {
if (Build.VERSION.SDK_INT >= 24)
provider.close()
else
@Suppress("deprecation")
provider.release()
}
}
@Suppress("unused")
private fun update_2_3() {
// Don't show a warning for Android updates anymore
accountManager.setUserData(account, "last_android_version", null)
var serviceCardDAV: Long? = null
var serviceCalDAV: Long? = null
ServiceDB.OpenHelper(context).use { dbHelper ->
val db = dbHelper.writableDatabase
// we have to create the WebDAV Service database only from the old address book, calendar and task list URLs
// CardDAV: migrate address books
context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)?.let { client ->
try {
val addrBook = LocalAddressBook(context, account, client)
val url = addrBook.getURL()
Logger.log.fine("Migrating address book $url")
// insert CardDAV service
val values = ContentValues(3)
values.put(Services.ACCOUNT_NAME, account.name)
values.put(Services.SERVICE, Services.SERVICE_CARDDAV)
serviceCardDAV = db.insert(Services._TABLE, null, values)
// insert address book
values.clear()
values.put(Collections.SERVICE_ID, serviceCardDAV)
values.put(Collections.URL, url)
values.put(Collections.SYNC, 1)
db.insert(Collections._TABLE, null, values)
// insert home set
HttpUrl.parse(url)?.let {
val homeSet = it.resolve("../")
values.clear()
values.put(HomeSets.SERVICE_ID, serviceCardDAV)
values.put(HomeSets.URL, homeSet.toString())
db.insert(HomeSets._TABLE, null, values)
}
} catch (e: ContactsStorageException) {
Logger.log.log(Level.SEVERE, "Couldn't migrate address book", e)
} finally {
if (Build.VERSION.SDK_INT >= 24)
client.close()
else
@Suppress("deprecation")
client.release()
}
}
// CalDAV: migrate calendars + task lists
val collections = HashSet<String>()
val homeSets = HashSet<HttpUrl>()
context.contentResolver.acquireContentProviderClient(CalendarContract.AUTHORITY)?.let { client ->
try {
val calendars = AndroidCalendar.find(account, client, LocalCalendar.Factory, null, null)
for (calendar in calendars)
calendar.name?.let { url ->
Logger.log.fine("Migrating calendar $url")
collections.add(url)
HttpUrl.parse(url)?.resolve("../")?.let { homeSets.add(it) }
}
} catch (e: CalendarStorageException) {
Logger.log.log(Level.SEVERE, "Couldn't migrate calendars", e)
} finally {
if (Build.VERSION.SDK_INT >= 24)
client.close()
else
@Suppress("deprecation")
client.release()
}
}
AndroidTaskList.acquireTaskProvider(context.contentResolver)?.use { provider ->
try {
val taskLists = AndroidTaskList.find(account, provider, LocalTaskList.Factory, null, null)
for (taskList in taskLists)
taskList.syncId?.let { url ->
Logger.log.fine("Migrating task list $url")
collections.add(url)
HttpUrl.parse(url)?.resolve("../")?.let { homeSets.add(it) }
}
} catch (e: CalendarStorageException) {
Logger.log.log(Level.SEVERE, "Couldn't migrate task lists", e)
}
}
if (!collections.isEmpty()) {
// insert CalDAV service
val values = ContentValues(3)
values.put(Services.ACCOUNT_NAME, account.name)
values.put(Services.SERVICE, Services.SERVICE_CALDAV)
serviceCalDAV = db.insert(Services._TABLE, null, values)
// insert collections
for (url in collections) {
values.clear()
values.put(Collections.SERVICE_ID, serviceCalDAV)
values.put(Collections.URL, url)
values.put(Collections.SYNC, 1)
db.insert(Collections._TABLE, null, values)
}
// insert home sets
for (homeSet in homeSets) {
values.clear()
values.put(HomeSets.SERVICE_ID, serviceCalDAV)
values.put(HomeSets.URL, homeSet.toString())
db.insert(HomeSets._TABLE, null, values)
}
}
}
// initiate service detection (refresh) to get display names, colors etc.
val refresh = Intent(context, DavService::class.java)
refresh.action = DavService.ACTION_REFRESH_COLLECTIONS
serviceCardDAV?.let {
refresh.putExtra(DavService.EXTRA_DAV_SERVICE_ID, it)
context.startService(refresh)
}
serviceCalDAV?.let {
refresh.putExtra(DavService.EXTRA_DAV_SERVICE_ID, it)
context.startService(refresh)
}
}
@Suppress("unused")
private fun update_3_4() {
setGroupMethod(GroupMethod.CATEGORIES)
}
/* Android 7.1.1 OpenTasks fix */
@Suppress("unused")
private fun update_4_5() {
// call PackageChangedReceiver which then enables/disables OpenTasks sync when it's (not) available
PackageChangedReceiver.updateTaskSync(context)
}
@Suppress("unused")
private fun update_5_6() {
context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)?.use { provider ->
// don't run syncs during the migration
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 0)
ContentResolver.setIsSyncable(account, App.addressBooksAuthority, 0)
ContentResolver.cancelSync(account, null)
val parcel = Parcel.obtain()
try {
// get previous address book settings (including URL)
val raw = ContactsContract.SyncState.get(provider, account)
if (raw == null)
Logger.log.info("No contacts sync state, ignoring account")
else {
parcel.unmarshall(raw, 0, raw.size)
parcel.setDataPosition(0)
val params = parcel.readBundle()
val url = params.getString("url")
if (url == null)
Logger.log.info("No address book URL, ignoring account")
else {
// create new address book
val info = CollectionInfo(url)
info.type = CollectionInfo.Type.ADDRESS_BOOK
info.displayName = account.name
Logger.log.log(Level.INFO, "Creating new address book account", url)
val addressBookAccount = Account(LocalAddressBook.accountName(account, info), App.addressBookAccountType)
if (!accountManager.addAccountExplicitly(addressBookAccount, null, LocalAddressBook.initialUserData(account, info.url)))
throw ContactsStorageException("Couldn't create address book account")
// move contacts to new address book
Logger.log.info("Moving contacts from $account to $addressBookAccount")
val newAccount = ContentValues(2)
newAccount.put(ContactsContract.RawContacts.ACCOUNT_NAME, addressBookAccount.name)
newAccount.put(ContactsContract.RawContacts.ACCOUNT_TYPE, addressBookAccount.type)
val affected = provider.update(ContactsContract.RawContacts.CONTENT_URI.buildUpon()
.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, account.name)
.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type)
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build(),
newAccount,
"${ContactsContract.RawContacts.ACCOUNT_NAME}=? AND ${ContactsContract.RawContacts.ACCOUNT_TYPE}=?",
arrayOf(account.name, account.type))
Logger.log.info("$affected contacts moved to new address book")
}
ContactsContract.SyncState.set(provider, account, null)
}
} catch(e: RemoteException) {
throw ContactsStorageException("Couldn't migrate contacts to new address book", e)
} finally {
parcel.recycle()
}
}
// update version number so that further syncs don't repeat the migration
accountManager.setUserData(account, KEY_SETTINGS_VERSION, "6")
// request sync of new address book account
ContentResolver.setIsSyncable(account, App.addressBooksAuthority, 1)
setSyncInterval(App.addressBooksAuthority, Constants.DEFAULT_SYNC_INTERVAL)
}
class AppUpdatedReceiver: BroadcastReceiver() {
@SuppressLint("UnsafeProtectedBroadcastReceiver,MissingPermission")
override fun onReceive(context: Context, intent: Intent?) {
Logger.log.info("DAVdroid was updated, checking for AccountSettings version")
// peek into AccountSettings to initiate a possible migration
val accountManager = AccountManager.get(context)
for (account in accountManager.getAccountsByType(context.getString(R.string.account_type)))
try {
Logger.log.info("Checking account ${account.name}")
AccountSettings(context, account)
} catch(e: InvalidAccountException) {
Logger.log.log(Level.SEVERE, "Couldn't check for updated account settings", e)
}
}
}
}
@@ -1,48 +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.accounts.AccountManager
import android.accounts.OnAccountsUpdateListener
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import java.util.*
class AccountsChangedReceiver: BroadcastReceiver() {
companion object {
private val listeners = LinkedList<OnAccountsUpdateListener>()
@JvmStatic
fun registerListener(listener: OnAccountsUpdateListener, callImmediately: Boolean) {
listeners += listener
if (callImmediately)
listener.onAccountsUpdated(null)
}
@JvmStatic
fun unregisterListener(listener: OnAccountsUpdateListener) {
listeners -= listener
}
}
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION) {
val serviceIntent = Intent(context, DavService::class.java)
serviceIntent.action = DavService.ACTION_ACCOUNTS_UPDATED
context.startService(serviceIntent)
for (listener in listeners)
listener.onAccountsUpdated(null)
}
}
}
@@ -0,0 +1,29 @@
package at.bitfire.davdroid
import android.content.Context
abstract class AndroidSingleton<T> {
var creatingSingleton = false
var singleton: T? = null
@Synchronized
fun getInstance(context: Context): T {
singleton?.let {
return it
}
if (creatingSingleton)
throw IllegalStateException("AndroidSingleton::getInstance() must not be called while createInstance()")
creatingSingleton = true
val newSingleton = createInstance(context.applicationContext)
singleton = newSingleton
creatingSingleton = false
return newSingleton
}
abstract fun createInstance(context: Context): T
}
+66 -36
View File
@@ -10,56 +10,86 @@ package at.bitfire.davdroid
import android.app.Application
import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.content.Intent
import android.net.Uri
import android.os.StrictMode
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.graphics.drawable.toBitmap
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.davdroid.ui.DebugInfoActivity
import at.bitfire.davdroid.ui.NotificationUtils
import at.bitfire.davdroid.ui.UiUtils
import java.util.logging.Level
import kotlin.concurrent.thread
import kotlin.system.exitProcess
class App: Application() {
@Suppress("unused")
class App: Application(), Thread.UncaughtExceptionHandler {
companion object {
@JvmField val FLAVOR_GOOGLE_PLAY = "gplay"
@JvmField val FLAVOR_ICLOUD = "icloud"
@JvmField val FLAVOR_SOLDUPE = "soldupe"
@JvmField val FLAVOR_STANDARD = "standard"
fun getLauncherBitmap(context: Context) =
AppCompatResources.getDrawable(context, R.mipmap.ic_launcher)?.toBitmap()
@JvmField val DISTRUST_SYSTEM_CERTIFICATES = "distrustSystemCerts"
@JvmField val LOG_TO_EXTERNAL_STORAGE = "logToExternalStorage"
@JvmField val OVERRIDE_PROXY = "overrideProxy"
@JvmField val OVERRIDE_PROXY_HOST = "overrideProxyHost"
@JvmField val OVERRIDE_PROXY_PORT = "overrideProxyPort"
@JvmField val OVERRIDE_PROXY_HOST_DEFAULT = "localhost"
@JvmField val OVERRIDE_PROXY_PORT_DEFAULT = 8118
lateinit var addressBookAccountType: String
lateinit var addressBooksAuthority: String
@JvmStatic
fun getLauncherBitmap(context: Context): Bitmap? {
val drawableLogo = if (android.os.Build.VERSION.SDK_INT >= 21)
context.getDrawable(R.mipmap.ic_launcher)
else
@Suppress("deprecation")
context.resources.getDrawable(R.mipmap.ic_launcher)
return if (drawableLogo is BitmapDrawable)
drawableLogo.bitmap
else
null
}
fun homepageUrl(context: Context) =
Uri.parse(context.getString(R.string.homepage_url)).buildUpon()
.appendQueryParameter("pk_campaign", BuildConfig.APPLICATION_ID)
.appendQueryParameter("pk_kwd", context::class.java.simpleName)
.appendQueryParameter("app-version", BuildConfig.VERSION_NAME)
.build()!!
}
override fun onCreate() {
super.onCreate()
Logger.initialize(this)
CustomCertificates.reinitCertManager(this)
Logger.reinitLogger(this)
if (BuildConfig.DEBUG)
// debug builds
StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder()
.detectActivityLeaks()
.detectFileUriExposure()
.detectLeakedClosableObjects()
.detectLeakedRegistrationObjects()
.detectLeakedSqlLiteObjects()
.penaltyLog()
.build())
else // if (BuildConfig.FLAVOR == FLAVOR_STANDARD)
// handle uncaught exceptions in non-debug standard flavor
Thread.setDefaultUncaughtExceptionHandler(this)
addressBookAccountType = getString(R.string.account_type_address_book)
addressBooksAuthority = getString(R.string.address_books_authority)
NotificationUtils.createChannels(this)
// don't block UI for some background checks
thread {
// create/update app shortcuts
UiUtils.updateShortcuts(this@App)
// watch installed/removed apps
TasksWatcher(this@App)
// check whether a tasks app is currently installed
TasksWatcher.updateTaskSync(this@App)
// check/repair sync intervals
AccountSettings.repairSyncIntervals(this@App)
// foreground service (possible workaround for devices which prevent DAVx5 from being started)
ForegroundService.startIfEnabled(this)
}
}
override fun uncaughtException(t: Thread, e: Throwable) {
Logger.log.log(Level.SEVERE, "Unhandled exception!", e)
}
val intent = Intent(this, DebugInfoActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.putExtra(DebugInfoActivity.EXTRA_CAUSE, e)
startActivity(intent)
exitProcess(1)
}
}
@@ -0,0 +1,22 @@
package at.bitfire.davdroid
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import at.bitfire.davdroid.log.Logger
/**
* There are circumstances when Android drops automatic sync of accounts and resets them
* to manual synchronization, for instance when the device is booted into safe mode.
*
* This receiver causes the app to be started when the device is rebooted. When the app
* is started, it checks (and repairs, if necessary) the sync intervals in [App.onCreate].
*/
class BootCompletedReceiver: BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Logger.log.info("Device has been rebooted; checking sync intervals etc.")
// sync intervals are checked in App.onCreate()
}
}
@@ -0,0 +1,12 @@
package at.bitfire.davdroid
import android.content.ContentProviderClient
import android.os.Build
@Suppress("DEPRECATION")
fun ContentProviderClient.closeCompat() {
if (Build.VERSION.SDK_INT >= 24)
close()
else
release()
}
@@ -5,20 +5,24 @@
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
package at.bitfire.davdroid;
package at.bitfire.davdroid
object Constants {
// notification IDs
@JvmField val NOTIFICATION_EXTERNAL_FILE_LOGGING = 1
@JvmField val NOTIFICATION_REFRESH_COLLECTIONS = 2
@JvmField val NOTIFICATION_CONTACTS_SYNC = 10
@JvmField val NOTIFICATION_CALENDAR_SYNC = 11
@JvmField val NOTIFICATION_TASK_SYNC = 12
@JvmField val NOTIFICATION_PERMISSIONS = 20
@JvmField val NOTIFICATION_SUBSCRIPTION = 21
const val DAVDROID_GREEN_RGBA = 0xFF8bc34a.toInt()
@JvmField
val DEFAULT_SYNC_INTERVAL = 4 * 3600L // 4 hours
/**
* Context label for [org.apache.commons.lang3.exception.ContextedException].
* Context value is the [at.bitfire.davdroid.resource.LocalResource]
* which is related to the exception cause.
*/
const val EXCEPTION_CONTEXT_LOCAL_RESOURCE = "localResource"
/**
* Context label for [org.apache.commons.lang3.exception.ContextedException].
* Context value is the [okhttp3.HttpUrl] of the remote resource
* which is related to the exception cause.
*/
const val EXCEPTION_CONTEXT_REMOTE_RESOURCE = "remoteResource"
}
@@ -1,41 +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.content.Context
import android.os.Build
import at.bitfire.cert4android.CustomCertManager
import at.bitfire.davdroid.model.ServiceDB
import at.bitfire.davdroid.model.Settings
import okhttp3.internal.tls.OkHostnameVerifier
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLSocketFactory
object CustomCertificates {
@JvmField var certManager: CustomCertManager? = null
var sslSocketFactoryCompat: SSLSocketFactory? = null
var hostnameVerifier: HostnameVerifier? = null
fun reinitCertManager(context: Context) {
if (BuildConfig.customCerts) {
certManager?.close()
ServiceDB.OpenHelper(context).use { dbHelper ->
val settings = Settings(dbHelper.readableDatabase)
val mgr = CustomCertManager(context.applicationContext, !settings.getBoolean(App.DISTRUST_SYSTEM_CERTIFICATES, false))
certManager = mgr
hostnameVerifier = mgr.hostnameVerifier(OkHostnameVerifier.INSTANCE)
sslSocketFactoryCompat = SSLSocketFactoryCompat(mgr)
}
}
}
}
@@ -9,66 +9,99 @@
package at.bitfire.davdroid
import android.accounts.Account
import android.accounts.AccountManager
import android.annotation.SuppressLint
import android.app.IntentService
import android.app.PendingIntent
import android.app.Service
import android.content.ContentValues
import android.content.ContentResolver
import android.content.Intent
import android.database.DatabaseUtils
import android.database.sqlite.SQLiteDatabase
import android.os.Binder
import android.support.v4.app.NotificationManagerCompat
import android.support.v7.app.NotificationCompat
import at.bitfire.dav4android.DavResource
import at.bitfire.dav4android.UrlUtils
import at.bitfire.dav4android.exception.DavException
import at.bitfire.dav4android.exception.HttpException
import at.bitfire.dav4android.property.*
import android.os.Bundle
import androidx.annotation.WorkerThread
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import at.bitfire.dav4jvm.DavResource
import at.bitfire.dav4jvm.Response
import at.bitfire.dav4jvm.UrlUtils
import at.bitfire.dav4jvm.exception.HttpException
import at.bitfire.dav4jvm.property.*
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.model.CollectionInfo
import at.bitfire.davdroid.model.ServiceDB.*
import at.bitfire.davdroid.model.ServiceDB.Collections
import at.bitfire.davdroid.resource.LocalAddressBook
import at.bitfire.davdroid.model.*
import at.bitfire.davdroid.model.Collection
import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.davdroid.ui.DebugInfoActivity
import at.bitfire.vcard4android.ContactsStorageException
import at.bitfire.davdroid.ui.NotificationUtils
import okhttp3.HttpUrl
import org.apache.commons.collections4.iterators.IteratorChain
import org.apache.commons.collections4.iterators.SingletonIterator
import java.io.IOException
import okhttp3.OkHttpClient
import java.lang.ref.WeakReference
import java.util.*
import java.util.logging.Level
import kotlin.concurrent.thread
import kotlin.collections.*
class DavService: Service() {
@Suppress("DEPRECATION")
class DavService: IntentService("DavService") {
companion object {
@JvmField val ACTION_ACCOUNTS_UPDATED = "accountsUpdated"
@JvmField val ACTION_REFRESH_COLLECTIONS = "refreshCollections"
@JvmField val EXTRA_DAV_SERVICE_ID = "davServiceID"
const val ACTION_REFRESH_COLLECTIONS = "refreshCollections"
const val EXTRA_DAV_SERVICE_ID = "davServiceID"
/** Initialize a forced synchronization. Expects intent data
to be an URI of this format:
contents://<authority>/<account.type>/<account name>
**/
const val ACTION_FORCE_SYNC = "forceSync"
val DAV_COLLECTION_PROPERTIES = arrayOf(
ResourceType.NAME,
CurrentUserPrivilegeSet.NAME,
DisplayName.NAME,
Owner.NAME,
AddressbookDescription.NAME, SupportedAddressData.NAME,
CalendarDescription.NAME, CalendarColor.NAME, SupportedCalendarComponentSet.NAME,
Source.NAME
)
}
private val runningRefresh = HashSet<Long>()
private val refreshingStatusListeners = LinkedList<WeakReference<RefreshingStatusListener>>()
/**
* List of [Service] IDs for which the collections are currently refreshed
*/
private val runningRefresh = Collections.synchronizedSet(HashSet<Long>())
/**
* Currently registered [RefreshingStatusListener]s, which will be notified
* when a collection refresh status changes
*/
private val refreshingStatusListeners = Collections.synchronizedList(LinkedList<WeakReference<RefreshingStatusListener>>())
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
intent?.let {
val id = intent.getLongExtra(EXTRA_DAV_SERVICE_ID, -1)
@WorkerThread
override fun onHandleIntent(intent: Intent?) {
if (intent == null)
return
when (intent.action) {
ACTION_ACCOUNTS_UPDATED ->
cleanupAccounts()
ACTION_REFRESH_COLLECTIONS ->
if (runningRefresh.add(id)) {
thread { refreshCollections(id) }
refreshingStatusListeners.forEach { it.get()?.onDavRefreshStatusChanged(id, true) }
val id = intent.getLongExtra(EXTRA_DAV_SERVICE_ID, -1)
when (intent.action) {
ACTION_REFRESH_COLLECTIONS ->
if (runningRefresh.add(id)) {
refreshingStatusListeners.forEach { listener ->
listener.get()?.onDavRefreshStatusChanged(id, true)
}
}
}
return START_NOT_STICKY
val db = AppDatabase.getInstance(this@DavService)
refreshCollections(db, id)
}
ACTION_FORCE_SYNC -> {
val uri = intent.data!!
val authority = uri.authority!!
val account = Account(
uri.pathSegments[1],
uri.pathSegments[0]
)
forceSync(authority, account)
}
}
}
@@ -85,17 +118,20 @@ class DavService: Service() {
inner class InfoBinder: Binder() {
fun isRefreshing(id: Long) = runningRefresh.contains(id)
fun addRefreshingStatusListener(listener: RefreshingStatusListener, callImmediate: Boolean) {
fun addRefreshingStatusListener(listener: RefreshingStatusListener, callImmediateIfRunning: Boolean) {
refreshingStatusListeners += WeakReference<RefreshingStatusListener>(listener)
if (callImmediate)
runningRefresh.forEach { id -> listener.onDavRefreshStatusChanged(id, true) }
if (callImmediateIfRunning)
synchronized(runningRefresh) {
for (id in runningRefresh)
listener.onDavRefreshStatusChanged(id, true)
}
}
fun removeRefreshingStatusListener(listener: RefreshingStatusListener) {
val iter = refreshingStatusListeners.iterator()
while (iter.hasNext()) {
val item = iter.next().get()
if (listener == item)
if (item == listener || item == null)
iter.remove()
}
}
@@ -109,203 +145,188 @@ class DavService: Service() {
which actually do the work
*/
@SuppressLint("MissingPermission")
fun cleanupAccounts() {
Logger.log.info("Cleaning up orphaned accounts")
OpenHelper(this).use { dbHelper ->
val db = dbHelper.writableDatabase
val sqlAccountNames = LinkedList<String>()
val accountNames = HashSet<String>()
val accountManager = AccountManager.get(this)
for (account in accountManager.getAccountsByType(getString(R.string.account_type))) {
sqlAccountNames.add(DatabaseUtils.sqlEscapeString(account.name))
accountNames += account.name
}
// delete orphaned address book accounts
accountManager.getAccountsByType(App.addressBookAccountType)
.map { LocalAddressBook(this, it, null) }
.forEach {
try {
if (!accountNames.contains(it.getMainAccount().name))
it.delete()
} catch(e: ContactsStorageException) {
Logger.log.log(Level.SEVERE, "Couldn't get address book main account", e)
}
}
// delete orphaned services in DB
if (sqlAccountNames.isEmpty())
db.delete(Services._TABLE, null, null)
else
db.delete(Services._TABLE, "${Services.ACCOUNT_NAME} NOT IN (${sqlAccountNames.joinToString(",")})", null)
}
private fun forceSync(authority: String, account: Account) {
Logger.log.info("Forcing $authority synchronization of $account")
val extras = Bundle(2)
extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true) // manual sync
extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true) // run immediately (don't queue)
ContentResolver.requestSync(account, authority, extras)
}
fun refreshCollections(service: Long) {
OpenHelper(this@DavService).use { dbHelper ->
val db = dbHelper.writableDatabase
private fun refreshCollections(db: AppDatabase, serviceId: Long) {
val homeSetDao = db.homeSetDao()
val collectionDao = db.collectionDao()
val serviceType by lazy {
db.query(Services._TABLE, arrayOf(Services.SERVICE), "${Services.ID}=?", arrayOf(service.toString()), null, null, null)?.use { cursor ->
if (cursor.moveToNext())
return@lazy cursor.getString(0)
} ?: throw IllegalArgumentException("Service not found")
}
val service = db.serviceDao().get(serviceId) ?: throw IllegalArgumentException("Service not found")
val account = Account(service.accountName, getString(R.string.account_type))
val account by lazy {
db.query(Services._TABLE, arrayOf(Services.ACCOUNT_NAME), "${Services.ID}=?", arrayOf(service.toString()), null, null, null)?.use { cursor ->
if (cursor.moveToNext())
return@lazy Account(cursor.getString(0), getString(R.string.account_type))
}
throw IllegalArgumentException("Account not found")
}
val homeSets = homeSetDao.getByService(serviceId).associateBy { it.url }.toMutableMap()
val collections = collectionDao.getByService(serviceId).associateBy { it.url }.toMutableMap()
val homeSets by lazy {
val homeSets = mutableSetOf<HttpUrl>()
db.query(HomeSets._TABLE, arrayOf(HomeSets.URL), "${HomeSets.SERVICE_ID}=?", arrayOf(service.toString()), null, null, null)?.use { cursor ->
while (cursor.moveToNext())
HttpUrl.parse(cursor.getString(0))?.let { homeSets += it }
}
homeSets
}
/**
* Checks if the given URL defines home sets and adds them to the home set list.
*
* @param personal Whether this is the "outer" call of the recursion.
*
* *true* = found home sets belong to the current-user-principal; recurse if
* calendar proxies or group memberships are found
*
* *false* = found home sets don't directly belong to the current-user-principal; don't recurse
*
* @throws java.io.IOException
* @throws HttpException
* @throws at.bitfire.dav4jvm.exception.DavException
*/
fun queryHomeSets(client: OkHttpClient, url: HttpUrl, personal: Boolean = true) {
val related = mutableSetOf<HttpUrl>()
val collections by lazy {
val collections = mutableMapOf<HttpUrl, CollectionInfo>()
db.query(Collections._TABLE, null, "${Collections.SERVICE_ID}=?", arrayOf(service.toString()), null, null, null)?.use { cursor ->
while (cursor.moveToNext()) {
val values = ContentValues()
DatabaseUtils.cursorRowToContentValues(cursor, values)
values.getAsString(Collections.URL)?.let { url ->
HttpUrl.parse(url)?.let { collections.put(it, CollectionInfo(values)) }
fun findRelated(root: HttpUrl, dav: Response) {
// refresh home sets: calendar-proxy-read/write-for
dav[CalendarProxyReadFor::class.java]?.let {
for (href in it.hrefs) {
Logger.log.fine("Principal is a read-only proxy for $href, checking for home sets")
root.resolve(href)?.let { proxyReadFor ->
related += proxyReadFor
}
}
}
collections
}
fun readPrincipal(): HttpUrl? {
db.query(Services._TABLE, arrayOf(Services.PRINCIPAL), "${Services.ID}=?", arrayOf(service.toString()), null, null, null)?.use { cursor ->
if (cursor.moveToNext())
cursor.getString(0)?.let { return HttpUrl.parse(it) }
}
return null
}
/**
* Checks if the given URL defines home sets and adds them to the home set list.
* @param dav DavResource to check
*/
@Throws(IOException::class, HttpException::class, DavException::class)
fun queryHomeSets(dav: DavResource) {
when (serviceType) {
Services.SERVICE_CARDDAV -> {
dav.propfind(0, AddressbookHomeSet.NAME, GroupMembership.NAME)
for ((resource, addressbookHomeSet) in dav.findProperties(AddressbookHomeSet.NAME) as List<Pair<DavResource, AddressbookHomeSet>>)
for (href in addressbookHomeSet.hrefs)
resource.location.resolve(href)?.let { homeSets += UrlUtils.withTrailingSlash(it) }
dav[CalendarProxyWriteFor::class.java]?.let {
for (href in it.hrefs) {
Logger.log.fine("Principal is a read/write proxy for $href, checking for home sets")
root.resolve(href)?.let { proxyWriteFor ->
related += proxyWriteFor
}
}
Services.SERVICE_CALDAV -> {
dav.propfind(0, CalendarHomeSet.NAME, CalendarProxyReadFor.NAME, CalendarProxyWriteFor.NAME, GroupMembership.NAME)
for ((resource, calendarHomeSet) in dav.findProperties(CalendarHomeSet.NAME) as List<Pair<DavResource, CalendarHomeSet>>)
for (href in calendarHomeSet.hrefs)
resource.location.resolve(href)?.let { homeSets.add(UrlUtils.withTrailingSlash(it)) }
}
// refresh home sets: direct group memberships
dav[GroupMembership::class.java]?.let {
for (href in it.hrefs) {
Logger.log.fine("Principal is member of group $href, checking for home sets")
root.resolve(href)?.let { groupMembership ->
related += groupMembership
}
}
}
}
fun saveHomeSets() {
db.delete(HomeSets._TABLE, "${HomeSets.SERVICE_ID}=?", arrayOf(service.toString()))
for (homeSet in homeSets) {
val values = ContentValues(2)
values.put(HomeSets.SERVICE_ID, service)
values.put(HomeSets.URL, homeSet.toString())
db.insertOrThrow(HomeSets._TABLE, null, values)
val dav = DavResource(client, url)
when (service.type) {
Service.TYPE_CARDDAV ->
try {
dav.propfind(0, DisplayName.NAME, AddressbookHomeSet.NAME, GroupMembership.NAME) { response, _ ->
response[AddressbookHomeSet::class.java]?.let { homeSet ->
for (href in homeSet.hrefs)
dav.location.resolve(href)?.let {
val foundUrl = UrlUtils.withTrailingSlash(it)
homeSets[foundUrl] = HomeSet(0, service.id, personal, foundUrl)
}
}
if (personal)
findRelated(dav.location, response)
}
} catch (e: HttpException) {
if (e.code/100 == 4)
Logger.log.log(Level.INFO, "Ignoring Client Error 4xx while looking for addressbook home sets", e)
else
throw e
}
Service.TYPE_CALDAV -> {
try {
dav.propfind(0, DisplayName.NAME, CalendarHomeSet.NAME, CalendarProxyReadFor.NAME, CalendarProxyWriteFor.NAME, GroupMembership.NAME) { response, _ ->
response[CalendarHomeSet::class.java]?.let { homeSet ->
for (href in homeSet.hrefs)
dav.location.resolve(href)?.let {
val foundUrl = UrlUtils.withTrailingSlash(it)
homeSets[foundUrl] = HomeSet(0, service.id, personal, foundUrl)
}
}
if (personal)
findRelated(dav.location, response)
}
} catch (e: HttpException) {
if (e.code/100 == 4)
Logger.log.log(Level.INFO, "Ignoring Client Error 4xx while looking for calendar home sets", e)
else
throw e
}
}
}
fun saveCollections() {
db.delete(Collections._TABLE, "${HomeSets.SERVICE_ID}=?", arrayOf(service.toString()))
for ((_,collection) in collections) {
val values = collection.toDB()
Logger.log.log(Level.FINE, "Saving collection", values)
values.put(Collections.SERVICE_ID, service)
db.insertWithOnConflict(Collections._TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE)
}
// query related homesets (those that do not belong to the current-user-principal)
for (resource in related)
queryHomeSets(client, resource, false)
}
fun saveHomesets() {
DaoTools(homeSetDao).syncAll(
homeSetDao.getByService(serviceId),
homeSets,
{ it.url })
}
fun saveCollections() {
DaoTools(collectionDao).syncAll(
collectionDao.getByService(serviceId),
collections, { it.url }) { new, old ->
new.forceReadOnly = old.forceReadOnly
new.sync = old.sync
}
}
try {
Logger.log.info("Refreshing ${service.type} collections of service #$service")
try {
Logger.log.info("Refreshing $serviceType collections of service #$service")
// cancel previous notification
NotificationManagerCompat.from(this)
.cancel(service.toString(), NotificationUtils.NOTIFY_REFRESH_COLLECTIONS)
// create authenticating OkHttpClient (credentials taken from account settings)
val httpClient = HttpClient.create(this@DavService, account)
// create authenticating OkHttpClient (credentials taken from account settings)
HttpClient.Builder(this, AccountSettings(this, account))
.setForeground(true)
.build().use { client ->
val httpClient = client.okHttpClient
// refresh home set list (from principal)
readPrincipal()?.let { principalUrl ->
service.principal?.let { principalUrl ->
Logger.log.fine("Querying principal $principalUrl for home sets")
val principal = DavResource(httpClient, principalUrl)
queryHomeSets(principal)
// refresh home sets: calendar-proxy-read/write-for
for ((resource, proxyRead) in principal.findProperties(CalendarProxyReadFor.NAME) as List<Pair<DavResource, CalendarProxyReadFor>>)
for (href in proxyRead.hrefs) {
Logger.log.fine("Principal is a read-only proxy for $href, checking for home sets")
resource.location.resolve(href)?.let { queryHomeSets(DavResource(httpClient, it)) }
}
for ((resource, proxyWrite) in principal.findProperties(CalendarProxyWriteFor.NAME) as List<Pair<DavResource, CalendarProxyWriteFor>>)
for (href in proxyWrite.hrefs) {
Logger.log.fine("Principal is a read/write proxy for $href, checking for home sets")
resource.location.resolve(href)?.let { queryHomeSets(DavResource(httpClient, it)) }
}
// refresh home sets: direct group memberships
(principal.properties[GroupMembership.NAME] as GroupMembership?)?.let { groupMembership ->
for (href in groupMembership.hrefs) {
Logger.log.fine("Principal is member of group $href, checking for home sets")
principal.location.resolve(href)?.let { url ->
try {
queryHomeSets(DavResource(httpClient, url))
} catch(e: HttpException) {
Logger.log.log(Level.WARNING, "Couldn't query member group", e)
} catch(e: DavException) {
Logger.log.log(Level.WARNING, "Couldn't query member group", e)
}
}
}
}
queryHomeSets(httpClient, principalUrl)
}
// remember selected collections
val selectedCollections = HashSet<HttpUrl>()
collections.values
.filter { it.selected }
.forEach { (url,_) -> HttpUrl.parse(url)?.let { selectedCollections.add(it) } }
// now refresh collections (taken from home sets)
// now refresh homesets and their member collections
val itHomeSets = homeSets.iterator()
while (itHomeSets.hasNext()) {
val homeSetUrl = itHomeSets.next()
Logger.log.fine("Listing home set $homeSetUrl")
val (homeSetUrl, homeSet) = itHomeSets.next()
Logger.log.fine("Listing home set ${homeSetUrl}")
val homeSet = DavResource(httpClient, homeSetUrl)
try {
homeSet.propfind(1, *CollectionInfo.DAV_PROPERTIES)
val itCollections = IteratorChain<DavResource>(homeSet.members.iterator(), SingletonIterator(homeSet))
while (itCollections.hasNext()) {
val member = itCollections.next()
val info = CollectionInfo(member)
DavResource(httpClient, homeSetUrl).propfind(1, *DAV_COLLECTION_PROPERTIES) { response, relation ->
if (!response.isSuccess())
return@propfind
if (relation == Response.HrefRelation.SELF) {
// this response is about the homeset itself
homeSet.displayName = response[DisplayName::class.java]?.displayName
homeSet.privBind = response[CurrentUserPrivilegeSet::class.java]?.mayBind ?: true
}
// in any case, check whether the response is about a useable collection
val info = Collection.fromDavResponse(response) ?: return@propfind
info.serviceId = serviceId
info.refHomeSet = homeSet
info.confirmed = true
info.owner = response[Owner::class.java]?.href?.let { response.href.resolve(it) }
Logger.log.log(Level.FINE, "Found collection", info)
if ((serviceType == Services.SERVICE_CARDDAV && info.type == CollectionInfo.Type.ADDRESS_BOOK) ||
(serviceType == Services.SERVICE_CALDAV && info.type == CollectionInfo.Type.CALENDAR))
collections[member.location] = info
// remember usable collections
if ((service.type == Service.TYPE_CARDDAV && info.type == Collection.TYPE_ADDRESSBOOK) ||
(service.type == Service.TYPE_CALDAV && arrayOf(Collection.TYPE_CALENDAR, Collection.TYPE_WEBCAL).contains(info.type)))
collections[response.href] = info
}
} catch(e: HttpException) {
if (e.status in arrayOf(403, 404, 410))
if (e.code in arrayOf(403, 404, 410))
// delete home set only if it was not accessible (40x)
itHomeSets.remove()
}
@@ -317,61 +338,69 @@ class DavService: Service() {
val (url, info) = itCollections.next()
if (!info.confirmed)
try {
val collection = DavResource(httpClient, url)
collection.propfind(0, *CollectionInfo.DAV_PROPERTIES)
val info = CollectionInfo(collection)
info.confirmed = true
// this collection doesn't belong to a homeset anymore, otherwise it would have been confirmed
info.homeSetId = null
// remove unusable collections
if ((serviceType == Services.SERVICE_CARDDAV && info.type != CollectionInfo.Type.ADDRESS_BOOK) ||
(serviceType == Services.SERVICE_CALDAV && info.type != CollectionInfo.Type.CALENDAR))
itCollections.remove()
DavResource(httpClient, url).propfind(0, *DAV_COLLECTION_PROPERTIES) { response, _ ->
if (!response.isSuccess())
return@propfind
val collection = Collection.fromDavResponse(response) ?: return@propfind
collection.confirmed = true
// remove unusable collections
if ((service.type == Service.TYPE_CARDDAV && collection.type != Collection.TYPE_ADDRESSBOOK) ||
(service.type == Service.TYPE_CALDAV && !arrayOf(Collection.TYPE_CALENDAR, Collection.TYPE_WEBCAL).contains(collection.type)) ||
(collection.type == Collection.TYPE_WEBCAL && collection.source == null))
itCollections.remove()
}
} catch(e: HttpException) {
if (e.status in arrayOf(403, 404, 410))
if (e.code in arrayOf(403, 404, 410))
// delete collection only if it was not accessible (40x)
itCollections.remove()
else
throw e
}
}
}
// restore selections
for (url in selectedCollections)
collections[url]?.let { it.selected = true }
db.runInTransaction {
saveHomesets()
db.beginTransactionNonExclusive()
try {
saveHomeSets()
saveCollections()
db.setTransactionSuccessful()
} finally {
db.endTransaction()
}
// use refHomeSet (if available) to determine homeset ID
for (collection in collections.values)
collection.refHomeSet?.let { homeSet ->
collection.homeSetId = homeSet.id
}
saveCollections()
}
} catch(e: InvalidAccountException) {
Logger.log.log(Level.SEVERE, "Invalid account", e)
} catch(e: Exception) {
Logger.log.log(Level.SEVERE, "Couldn't refresh collection list", e)
} catch(e: InvalidAccountException) {
Logger.log.log(Level.SEVERE, "Invalid account", e)
} catch(e: Exception) {
Logger.log.log(Level.SEVERE, "Couldn't refresh collection list", e)
val debugIntent = Intent(this@DavService, DebugInfoActivity::class.java)
debugIntent.putExtra(DebugInfoActivity.KEY_THROWABLE, e)
debugIntent.putExtra(DebugInfoActivity.KEY_ACCOUNT, account)
val debugIntent = Intent(this, DebugInfoActivity::class.java)
debugIntent.putExtra(DebugInfoActivity.EXTRA_CAUSE, e)
debugIntent.putExtra(DebugInfoActivity.EXTRA_ACCOUNT, account)
val nm = NotificationManagerCompat.from(this@DavService)
val notify = NotificationCompat.Builder(this@DavService)
.setSmallIcon(R.drawable.ic_error_light)
.setLargeIcon(App.getLauncherBitmap(this@DavService))
.setContentTitle(getString(R.string.dav_service_refresh_failed))
.setContentText(getString(R.string.dav_service_refresh_couldnt_refresh))
.setContentIntent(PendingIntent.getActivity(this@DavService, 0, debugIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.build()
nm.notify(Constants.NOTIFICATION_REFRESH_COLLECTIONS, notify)
} finally {
runningRefresh.remove(service)
refreshingStatusListeners.forEach { it.get()?.onDavRefreshStatusChanged(service, false) }
val notify = NotificationUtils.newBuilder(this, NotificationUtils.CHANNEL_GENERAL)
.setSmallIcon(R.drawable.ic_sync_problem_notify)
.setContentTitle(getString(R.string.dav_service_refresh_failed))
.setContentText(getString(R.string.dav_service_refresh_couldnt_refresh))
.setContentIntent(PendingIntent.getActivity(this, 0, debugIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setSubText(account.name)
.setCategory(NotificationCompat.CATEGORY_ERROR)
.build()
NotificationManagerCompat.from(this)
.notify(serviceId.toString(), NotificationUtils.NOTIFY_REFRESH_COLLECTIONS, notify)
} finally {
runningRefresh.remove(serviceId)
refreshingStatusListeners.mapNotNull { it.get() }.forEach {
it.onDavRefreshStatusChanged(serviceId, false)
}
}
}
}
}
+178 -12
View File
@@ -6,33 +6,199 @@
* http://www.gnu.org/licenses/gpl.html
*/
package at.bitfire.davdroid;
package at.bitfire.davdroid
import android.accounts.Account
import android.annotation.TargetApi
import android.content.ContentResolver
import android.content.Context
import android.net.ConnectivityManager
import android.os.Build
import android.os.Bundle
import android.provider.CalendarContract
import android.provider.ContactsContract
import androidx.core.content.getSystemService
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.resource.LocalAddressBook
import at.bitfire.davdroid.resource.TaskUtils
import okhttp3.HttpUrl
import org.xbill.DNS.*
import java.util.*
/**
* Some WebDAV and related network utility methods
*/
object DavUtils {
@JvmStatic
enum class SyncStatus {
ACTIVE, PENDING, IDLE
}
@Suppress("FunctionName")
fun ARGBtoCalDAVColor(colorWithAlpha: Int): String {
val alpha = (colorWithAlpha shr 24) and 0xFF
val color = colorWithAlpha and 0xFFFFFF
return String.format("#%06X%02X", color, alpha)
return String.format(Locale.ROOT, "#%06X%02X", color, alpha)
}
@JvmStatic
fun lastSegmentOfUrl(url: String): String {
val httpUrl = HttpUrl.parse(url) ?: throw IllegalArgumentException("url not parsable")
fun lastSegmentOfUrl(url: HttpUrl): String {
// the list returned by HttpUrl.pathSegments() is unmodifiable, so we have to create a copy
val segments = LinkedList<String>(httpUrl.pathSegments())
Collections.reverse(segments)
val segments = LinkedList<String>(url.pathSegments)
segments.reverse()
for (segment in segments)
if (segment.isNotEmpty())
return segment
return segments.firstOrNull { it.isNotEmpty() } ?: "/"
}
return "/"
fun prepareLookup(context: Context, lookup: Lookup) {
@TargetApi(Build.VERSION_CODES.O)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
/* Since Android 8, the system properties net.dns1, net.dns2, ... are not available anymore.
The current version of dnsjava relies on these properties to find the default name servers,
so we have to add the servers explicitly (fortunately, there's an Android API to
get the active DNS servers). */
val connectivity = context.getSystemService<ConnectivityManager>()!!
val activeLink = connectivity.getLinkProperties(connectivity.activeNetwork)
if (activeLink != null) {
// get DNS servers of active network link and set them for dnsjava so that it can send SRV queries
val simpleResolvers = activeLink.dnsServers.map {
Logger.log.fine("Using DNS server ${it.hostAddress}")
val resolver = SimpleResolver()
resolver.setAddress(it)
resolver
}
val resolver = ExtendedResolver(simpleResolvers.toTypedArray())
lookup.setResolver(resolver)
} else
Logger.log.severe("Couldn't determine DNS servers, dnsjava queries (SRV/TXT records) won't work")
}
}
fun selectSRVRecord(records: Array<out Record>?): SRVRecord? {
if (records == null)
return null
val srvRecords = records.filterIsInstance(SRVRecord::class.java)
if (srvRecords.size <= 1)
return srvRecords.firstOrNull()
/* RFC 2782
Priority
The priority of this target host. A client MUST attempt to
contact the target host with the lowest-numbered priority it can
reach; target hosts with the same priority SHOULD be tried in an
order defined by the weight field. [...]
Weight
A server selection mechanism. The weight field specifies a
relative weight for entries with the same priority. [...]
To select a target to be contacted next, arrange all SRV RRs
(that have not been ordered yet) in any order, except that all
those with weight 0 are placed at the beginning of the list.
Compute the sum of the weights of those RRs, and with each RR
associate the running sum in the selected order. Then choose a
uniform random number between 0 and the sum computed
(inclusive), and select the RR whose running sum value is the
first in the selected order which is greater than or equal to
the random number selected. The target host specified in the
selected SRV RR is the next one to be contacted by the client.
*/
val minPriority = srvRecords.map { it.priority }.minOrNull()
val useableRecords = srvRecords.filter { it.priority == minPriority }.sortedBy { it.weight != 0 }
val map = TreeMap<Int, SRVRecord>()
var runningWeight = 0
for (record in useableRecords) {
val weight = record.weight
runningWeight += weight
map[runningWeight] = record
}
val selector = (0..runningWeight).random()
return map.ceilingEntry(selector)!!.value
}
fun pathsFromTXTRecords(records: Array<Record>?): List<String> {
val paths = LinkedList<String>()
records?.filterIsInstance(TXTRecord::class.java)?.forEach { txt ->
@Suppress("UNCHECKED_CAST")
for (segment in txt.strings as List<String>)
if (segment.startsWith("path=")) {
paths.add(segment.substring(5))
break
}
}
return paths
}
/**
* Returns the sync status of a given account. Checks the account itself and possible
* sub-accounts (address book accounts).
*
* @param authorities sync authorities to check (usually taken from [syncAuthorities])
*
* @return sync status of the given account
*/
fun accountSyncStatus(context: Context, authorities: Iterable<String>, account: Account): SyncStatus {
// check active syncs
if (authorities.any { ContentResolver.isSyncActive(account, it) })
return SyncStatus.ACTIVE
val addrBookAccounts = LocalAddressBook.findAll(context, null, account).map { it.account }
if (addrBookAccounts.any { ContentResolver.isSyncActive(it, ContactsContract.AUTHORITY) })
return SyncStatus.ACTIVE
// check get pending syncs
if (authorities.any { ContentResolver.isSyncPending(account, it) } ||
addrBookAccounts.any { ContentResolver.isSyncPending(it, ContactsContract.AUTHORITY) })
return SyncStatus.PENDING
return SyncStatus.IDLE
}
/**
* Requests an immediate, manual sync of all available authorities for the given account.
*
* @param account account to sync
*/
fun requestSync(context: Context, account: Account) {
for (authority in syncAuthorities(context)) {
val extras = Bundle(2)
extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true) // manual sync
extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true) // run immediately (don't queue)
ContentResolver.requestSync(account, authority, extras)
}
}
/**
* Returns a list of all available sync authorities for main accounts (!= address book accounts):
*
* 1. address books authority (not [ContactsContract.AUTHORITY], but the one which manages address book accounts)
* 1. calendar authority
* 1. tasks authority (if available)
*
* Checking the availability of authorities may be relatively expensive, so the
* result should be cached for the current operation.
*
* @return list of available sync authorities for main accounts
*/
fun syncAuthorities(context: Context): List<String> {
val result = mutableListOf(
context.getString(R.string.address_books_authority),
CalendarContract.AUTHORITY
)
TaskUtils.currentProvider(context)?.let { taskProvider ->
result += taskProvider.authority
}
return result
}
}
@@ -0,0 +1,62 @@
package at.bitfire.davdroid
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import at.bitfire.davdroid.settings.Settings
import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.davdroid.ui.AppSettingsActivity
import at.bitfire.davdroid.ui.NotificationUtils
class ForegroundService : Service() {
companion object {
/**
* Starts/stops a foreground service, according to the app setting [Settings.FOREGROUND_SERVICE].
*/
const val ACTION_FOREGROUND = "foreground"
fun isEnabled(context: Context): Boolean {
val settings = SettingsManager.getInstance(context)
return settings.getBooleanOrNull(Settings.FOREGROUND_SERVICE) == true
}
fun startIfEnabled(context: Context) {
if (isEnabled(context)) {
val serviceIntent = Intent(ForegroundService.ACTION_FOREGROUND, null, context, ForegroundService::class.java)
if (Build.VERSION.SDK_INT >= 26)
context.startForegroundService(serviceIntent)
else
context.startService(serviceIntent)
}
}
}
override fun onBind(intent: Intent?) = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (isEnabled(this)) {
val settingsIntent = Intent(this, AppSettingsActivity::class.java).apply {
putExtra(AppSettingsActivity.EXTRA_SCROLL_TO, Settings.FOREGROUND_SERVICE)
}
val builder = NotificationCompat.Builder(this, NotificationUtils.CHANNEL_STATUS)
.setSmallIcon(R.drawable.ic_foreground_notify)
.setContentTitle(getString(R.string.foreground_service_notify_title))
.setContentText(getString(R.string.foreground_service_notify_text))
.setStyle(NotificationCompat.BigTextStyle())
.setContentIntent(PendingIntent.getActivity(this, 0, settingsIntent, PendingIntent.FLAG_UPDATE_CURRENT))
startForeground(NotificationUtils.NOTIFY_FOREGROUND, builder.build())
return START_STICKY
} else {
stopForeground(true)
return START_NOT_STICKY
}
}
}
@@ -6,139 +6,236 @@
* http://www.gnu.org/licenses/gpl.html
*/
package at.bitfire.davdroid;
package at.bitfire.davdroid
import android.accounts.Account
import android.content.Context
import android.os.Build
import at.bitfire.dav4android.BasicDigestAuthHandler
import at.bitfire.dav4android.UrlUtils
import android.security.KeyChain
import at.bitfire.cert4android.CustomCertManager
import at.bitfire.dav4jvm.BasicDigestAuthHandler
import at.bitfire.dav4jvm.UrlUtils
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.model.ServiceDB
import at.bitfire.davdroid.model.Settings
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import at.bitfire.davdroid.model.Credentials
import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.davdroid.settings.Settings
import at.bitfire.davdroid.settings.SettingsManager
import okhttp3.*
import okhttp3.brotli.BrotliInterceptor
import okhttp3.internal.tls.OkHostnameVerifier
import okhttp3.logging.HttpLoggingInterceptor
import java.io.File
import java.net.InetSocketAddress
import java.net.Proxy
import java.net.Socket
import java.security.KeyStore
import java.security.Principal
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.TimeUnit
import java.util.logging.Level
import javax.net.ssl.*
class HttpClient private constructor() {
class HttpClient private constructor(
val okHttpClient: OkHttpClient,
private val certManager: CustomCertManager?
): AutoCloseable {
companion object {
/** max. size of disk cache (10 MB) */
const val DISK_CACHE_MAX_SIZE: Long = 10*1024*1024
private val client = OkHttpClient()
private val userAgentInterceptor = UserAgentInterceptor()
/** [OkHttpClient] singleton to build all clients from */
val sharedClient: OkHttpClient = OkHttpClient.Builder()
// Set timeouts. According to [AbstractThreadedSyncAdapter], when there is no network
// traffic within a minute, a sync will be cancelled.
.connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS)
.pingInterval(45, TimeUnit.SECONDS) // avoid cancellation because of missing traffic; only works for HTTP/2
private val productName = when(BuildConfig.FLAVOR) {
App.FLAVOR_ICLOUD -> "MultiSync for Cloud"
App.FLAVOR_SOLDUPE -> "Soldupe Sync"
else -> "DAVdroid"
}
private val userAgentDate = SimpleDateFormat("yyyy/MM/dd", Locale.US).format(Date(BuildConfig.buildTime))
private val userAgent = "$productName/${BuildConfig.VERSION_NAME} ($userAgentDate; dav4android; okhttp3) Android/${Build.VERSION.RELEASE}"
// keep TLS 1.0 and 1.1 for now; remove when major browsers have dropped it (probably 2020)
.connectionSpecs(listOf(
ConnectionSpec.CLEARTEXT,
ConnectionSpec.COMPATIBLE_TLS
))
// don't allow redirects by default, because it would break PROPFIND handling
.followRedirects(false)
// offer Brotli and gzip compression
.addInterceptor(BrotliInterceptor)
// add User-Agent to every request
.addNetworkInterceptor(UserAgentInterceptor)
.build()
}
@JvmStatic
@JvmOverloads
fun create(context: Context?, settings: AccountSettings? = null, logger: java.util.logging.Logger = Logger.log): OkHttpClient {
var builder = defaultBuilder(context, logger)
override fun close() {
okHttpClient.cache?.close()
certManager?.close()
}
// use account settings for authentication
settings?.let {
val userName = it.username()
val password = it.password()
if (userName != null && password != null)
builder = addAuthentication(builder, null, userName, password)
}
class Builder(
val context: Context? = null,
accountSettings: AccountSettings? = null,
val logger: java.util.logging.Logger = Logger.log
) {
private var certManager: CustomCertManager? = null
private var certificateAlias: String? = null
private var cache: Cache? = null
return builder.build()
}
@JvmStatic
@Throws(InvalidAccountException::class)
fun create(context: Context, account: Account) =
create(context, AccountSettings(context, account))
private fun defaultBuilder(context: Context?, logger: java.util.logging.Logger): OkHttpClient.Builder {
val builder = client.newBuilder()
// use MemorizingTrustManager to manage self-signed certificates
if (CustomCertificates.sslSocketFactoryCompat != null && CustomCertificates.certManager != null)
builder.sslSocketFactory(CustomCertificates.sslSocketFactoryCompat, CustomCertificates.certManager)
CustomCertificates.hostnameVerifier?.let { builder.hostnameVerifier(it) }
// set timeouts
builder.connectTimeout(30, TimeUnit.SECONDS)
builder.writeTimeout(30, TimeUnit.SECONDS)
builder.readTimeout(120, TimeUnit.SECONDS)
// don't allow redirects, because it would break PROPFIND handling
builder.followRedirects(false)
// custom proxy support
context?.let {
ServiceDB.OpenHelper(it).use { dbHelper ->
try {
val settings = Settings(dbHelper.readableDatabase)
if (settings.getBoolean(App.OVERRIDE_PROXY, false)) {
val address = InetSocketAddress(
settings.getString(App.OVERRIDE_PROXY_HOST, App.OVERRIDE_PROXY_HOST_DEFAULT),
settings.getInt(App.OVERRIDE_PROXY_PORT, App.OVERRIDE_PROXY_PORT_DEFAULT)
)
val proxy = Proxy(Proxy.Type.HTTP, address)
builder.proxy(proxy)
Logger.log.log(Level.INFO, "Using proxy", proxy)
}
} catch(e: Exception) {
Logger.log.log(Level.SEVERE, "Can't set proxy, ignoring", e)
}
}
}
// add User-Agent to every request
builder.addNetworkInterceptor(userAgentInterceptor)
private val orig = sharedClient.newBuilder()
init {
// add cookie store for non-persistent cookies (some services like Horde use cookies for session tracking)
builder.cookieJar(MemoryCookieStore())
orig.cookieJar(MemoryCookieStore())
// add network logging, if requested
if (logger.isLoggable(Level.FINEST)) {
val loggingInterceptor = HttpLoggingInterceptor(HttpLoggingInterceptor.Logger {
message -> logger.finest(message)
})
val loggingInterceptor = HttpLoggingInterceptor { message -> logger.finest(message) }
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
builder.addInterceptor(loggingInterceptor)
orig.addInterceptor(loggingInterceptor)
}
return builder
if (context != null) {
val settings = SettingsManager.getInstance(context)
// custom proxy support
try {
if (settings.getBoolean(Settings.OVERRIDE_PROXY)) {
val address = InetSocketAddress(
settings.getString(Settings.OVERRIDE_PROXY_HOST),
settings.getInt(Settings.OVERRIDE_PROXY_PORT)
)
val proxy = Proxy(Proxy.Type.HTTP, address)
orig.proxy(proxy)
Logger.log.log(Level.INFO, "Using proxy", proxy)
}
} catch (e: Exception) {
Logger.log.log(Level.SEVERE, "Can't set proxy, ignoring", e)
}
// TODO don't instantiate CustomCertManager in .Builder (causes service leaks)
customCertManager(CustomCertManager(context, true /*BuildConfig.customCertsUI*/,
!(settings.getBoolean(Settings.DISTRUST_SYSTEM_CERTIFICATES))))
}
// use account settings for authentication
accountSettings?.let {
addAuthentication(null, it.credentials())
}
}
fun addAuthentication(builder: OkHttpClient.Builder, host: String?, username: String, password: String): OkHttpClient.Builder {
val authHandler = BasicDigestAuthHandler(UrlUtils.hostToDomain(host), username, password);
return builder
.addNetworkInterceptor(authHandler)
.authenticator(authHandler)
constructor(context: Context, host: String?, credentials: Credentials): this(context) {
addAuthentication(host, credentials)
}
@JvmOverloads
@JvmStatic
fun addAuthentication(client: OkHttpClient, host: String? = null, username: String, password: String): OkHttpClient {
val builder = client.newBuilder()
addAuthentication(builder, host, username, password)
return builder.build()
fun withDiskCache(context: Context): Builder {
for (dir in arrayOf(context.externalCacheDir, context.cacheDir).filterNotNull()) {
if (dir.exists() && dir.canWrite()) {
val cacheDir = File(dir, "HttpClient")
cacheDir.mkdir()
Logger.log.fine("Using disk cache: $cacheDir")
orig.cache(Cache(cacheDir, DISK_CACHE_MAX_SIZE))
break
}
}
return this
}
fun followRedirects(follow: Boolean): Builder {
orig.followRedirects(follow)
return this
}
fun customCertManager(manager: CustomCertManager) {
certManager = manager
}
fun setForeground(foreground: Boolean): Builder {
certManager?.appInForeground = foreground
return this
}
fun addAuthentication(host: String?, credentials: Credentials): Builder {
if (credentials.userName != null && credentials.password != null) {
val authHandler = BasicDigestAuthHandler(UrlUtils.hostToDomain(host), credentials.userName, credentials.password)
orig .addNetworkInterceptor(authHandler)
.authenticator(authHandler)
}
if (credentials.certificateAlias != null)
certificateAlias = credentials.certificateAlias
return this
}
fun build(): HttpClient {
val trustManager = certManager ?: {
val factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
factory.init(null as KeyStore?)
factory.trustManagers.first() as X509TrustManager
}()
val hostnameVerifier = certManager?.hostnameVerifier(OkHostnameVerifier)
?: OkHostnameVerifier
var keyManager: KeyManager? = null
certificateAlias?.let { alias ->
val context = requireNotNull(context)
// get provider certificate and private key
val certs = KeyChain.getCertificateChain(context, alias) ?: return@let
val key = KeyChain.getPrivateKey(context, alias) ?: return@let
logger.fine("Using provider certificate $alias for authentication (chain length: ${certs.size})")
// create KeyManager
keyManager = 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?) =
certs.takeIf { forAlias == alias }
override fun getPrivateKey(forAlias: String?) =
key.takeIf { forAlias == alias }
}
// HTTP/2 doesn't support client certificates (yet)
// see https://tools.ietf.org/html/draft-ietf-httpbis-http2-secondary-certs-04
orig.protocols(listOf(Protocol.HTTP_1_1))
}
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(
if (keyManager != null) arrayOf(keyManager) else null,
arrayOf(trustManager),
null)
orig.sslSocketFactory(sslContext.socketFactory, trustManager)
orig.hostnameVerifier(hostnameVerifier)
return HttpClient(orig.build(), certManager)
}
}
private class UserAgentInterceptor: Interceptor {
private object UserAgentInterceptor: Interceptor {
// use Locale.ROOT because numbers may be encoded as non-ASCII characters in other locales
private val userAgentDateFormat = SimpleDateFormat("yyyy/MM/dd", Locale.ROOT)
private val userAgentDate = userAgentDateFormat.format(Date(BuildConfig.buildTime))
private val userAgent = "${BuildConfig.userAgent}/${BuildConfig.VERSION_NAME} ($userAgentDate; dav4jvm; " +
"okhttp/${OkHttp.VERSION}) Android/${Build.VERSION.RELEASE}"
init {
Logger.log.info("Will set \"User-Agent: $userAgent\" for further requests")
}
override fun intercept(chain: Interceptor.Chain): Response {
val locale = Locale.getDefault()
@@ -27,12 +27,12 @@ class MemoryCookieStore: CookieJar {
* This ensures that cookies can be overwritten. [RFC 6265 5.3 Storage Model]
* Not thread-safe!
*/
val storage = MultiKeyMap.multiKeyMap(HashedMap<MultiKey<out String>, Cookie>())!!
private val storage = MultiKeyMap.multiKeyMap(HashedMap<MultiKey<out String>, Cookie>())!!
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
synchronized(storage) {
for (cookie in cookies)
storage.put(cookie.name(), cookie.domain(), cookie.path(), cookie)
storage.put(cookie.name, cookie.domain, cookie.path, cookie)
}
}
@@ -46,7 +46,7 @@ class MemoryCookieStore: CookieJar {
val cookie = iter.value
// remove expired cookies
if (cookie.expiresAt() <= System.currentTimeMillis()) {
if (cookie.expiresAt <= System.currentTimeMillis()) {
iter.remove()
continue
}
@@ -1,65 +1,25 @@
/*
* 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.accounts.Account
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.os.Bundle
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.model.ServiceDB
import at.bitfire.davdroid.model.ServiceDB.Services
import at.bitfire.davdroid.resource.LocalTaskList
import at.bitfire.ical4android.TaskProvider
import android.content.IntentFilter
class PackageChangedReceiver: BroadcastReceiver() {
abstract class PackageChangedReceiver(
val context: Context
): BroadcastReceiver(), AutoCloseable {
@SuppressLint("MissingPermission")
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_PACKAGE_ADDED || intent.action == Intent.ACTION_PACKAGE_FULLY_REMOVED)
updateTaskSync(context)
}
companion object {
@JvmStatic
fun updateTaskSync(context: Context) {
val tasksInstalled = LocalTaskList.tasksProviderAvailable(context)
Logger.log.info("Package (un)installed; OpenTasks provider now available = $tasksInstalled")
// check all accounts and (de)activate OpenTasks if a CalDAV service is defined
ServiceDB.OpenHelper(context).use { dbHelper ->
val db = dbHelper.readableDatabase
db.query(Services._TABLE, arrayOf(Services.ACCOUNT_NAME),
"${Services.SERVICE}=?", arrayOf(Services.SERVICE_CALDAV), null, null, null)?.use { cursor ->
while (cursor.moveToNext()) {
val account = Account(cursor.getString(0), context.getString(R.string.account_type))
if (tasksInstalled) {
if (ContentResolver.getIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority) <= 0) {
ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 1)
ContentResolver.setSyncAutomatically(account, TaskProvider.ProviderName.OpenTasks.authority, true)
ContentResolver.addPeriodicSync(account, TaskProvider.ProviderName.OpenTasks.authority, Bundle(), Constants.DEFAULT_SYNC_INTERVAL.toLong())
}
} else
ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 0)
}
}
}
init {
val filter = IntentFilter(Intent.ACTION_PACKAGE_ADDED).apply {
addAction(Intent.ACTION_PACKAGE_CHANGED)
addAction(Intent.ACTION_PACKAGE_REMOVED)
addDataScheme("package")
}
context.registerReceiver(this, filter)
}
}
override fun close() {
context.unregisterReceiver(this)
}
}
@@ -0,0 +1,126 @@
package at.bitfire.davdroid
import android.Manifest
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.location.LocationManager
import android.net.Uri
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.core.location.LocationManagerCompat
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.ui.NotificationUtils
import at.bitfire.davdroid.ui.PermissionsActivity
object PermissionUtils {
val CONTACT_PERMISSIONS = arrayOf(
Manifest.permission.READ_CONTACTS,
Manifest.permission.WRITE_CONTACTS
)
val CALENDAR_PERMISSIONS = arrayOf(
Manifest.permission.READ_CALENDAR,
Manifest.permission.WRITE_CALENDAR
)
val WIFI_SSID_PERMISSIONS =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1)
arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION)
else
arrayOf()
/**
* Checks whether all conditions to access the current WiFi's SSID are met:
*
* 1. location permissions ([WIFI_SSID_PERMISSIONS]) granted (Android 8.1+)
* 2. location enabled (Android 9+)
*
* @return *true* if SSID can be obtained; *false* if the SSID will be <unknown> or something like that
*/
fun canAccessWifiSsid(context: Context): Boolean {
// before Android 8.1, SSIDs are always readable
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1)
return true
val locationAvailable =
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P)
true // Android <9 doesn't require active location services
else
ContextCompat.getSystemService(context, LocationManager::class.java)?.let { locationManager ->
LocationManagerCompat.isLocationEnabled(locationManager)
} ?: /* location feature not available on this device */ false
return havePermissions(context, WIFI_SSID_PERMISSIONS) &&
locationAvailable
}
/**
* Whether this app declares the given permission (regardless of whether it has been granted or not).
*
* @param permisssion permission to check
*
* @return *true* if this app declares [permission] in the manifest; *false* otherwise
*/
fun declaresPermission(packageManager: PackageManager, permission: String): Boolean {
val info = packageManager.getPackageInfo(BuildConfig.APPLICATION_ID, PackageManager.GET_PERMISSIONS)
return info.requestedPermissions.contains(permission)
}
/**
* Checks whether at least one of the given permissions is granted.
*
* @param context context to check
* @param permissions array of permissions to check
*
* @return whether at least one of [permissions] is granted
*/
fun haveAnyPermission(context: Context, permissions: Array<String>) =
permissions.any { ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED }
/**
* Checks whether all given permissions are granted.
*
* @param context context to check
* @param permissions array of permissions to check
*
* @return whether all [permissions] are granted
*/
fun havePermissions(context: Context, permissions: Array<String>) =
permissions.all { ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED }
/**
* Shows a notification about missing permissions.
*
* @param context notification context
* @param intent will be set as content Intent; if null, an Intent to launch PermissionsActivity will be used
*/
fun notifyPermissions(context: Context, intent: Intent?) {
val contentIntent = intent ?: Intent(context, PermissionsActivity::class.java)
val notify = NotificationUtils.newBuilder(context, NotificationUtils.CHANNEL_SYNC_ERRORS)
.setSmallIcon(R.drawable.ic_sync_problem_notify)
.setContentTitle(context.getString(R.string.sync_error_permissions))
.setContentText(context.getString(R.string.sync_error_permissions_text))
.setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setCategory(NotificationCompat.CATEGORY_ERROR)
.setAutoCancel(true)
.build()
NotificationManagerCompat.from(context)
.notify(NotificationUtils.NOTIFY_PERMISSIONS, notify)
}
fun showAppSettings(context: Context) {
val intent = Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", BuildConfig.APPLICATION_ID, null))
if (intent.resolveActivity(context.packageManager) != null)
context.startActivity(intent)
else
Logger.log.warning("App settings Intent not resolvable")
}
}
@@ -1,160 +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.os.Build
import at.bitfire.davdroid.log.Logger
import java.io.IOException
import java.net.InetAddress
import java.net.Socket
import java.security.GeneralSecurityException
import java.util.*
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager
class SSLSocketFactoryCompat(trustManager: X509TrustManager): SSLSocketFactory() {
private var delegate: SSLSocketFactory
companion object {
// Android 5.0+ (API level 21) provides reasonable default settings
// but it still allows SSLv3
// https://developer.android.com/reference/javax/net/ssl/SSLSocket.html
var protocols: Array<String>? = null
var cipherSuites: Array<String>? = null
init {
if (Build.VERSION.SDK_INT >= 23) {
// Since Android 6.0 (API level 23),
// - TLSv1.1 and TLSv1.2 is enabled by default
// - SSLv3 is disabled by default
// - all modern ciphers are activated by default
protocols = null
cipherSuites = null
Logger.log.fine("Using device default TLS protocols/ciphers")
} else {
val socket = SSLSocketFactory.getDefault().createSocket() as SSLSocket?
try {
socket?.let {
/* set reasonable protocol versions */
// - enable all supported protocols (enables TLSv1.1 and TLSv1.2 on Android <5.0)
// - remove all SSL versions (especially SSLv3) because they're insecure now
val _protocols = LinkedList<String>()
for (protocol in socket.supportedProtocols.filterNot { it.contains("SSL", true) })
_protocols += protocol
Logger.log.info("Enabling (only) these TLS protocols: ${_protocols.joinToString(", ")}")
protocols = _protocols.toTypedArray()
/* set up reasonable cipher suites */
val knownCiphers = arrayOf<String>(
// TLS 1.2
"TLS_RSA_WITH_AES_256_GCM_SHA384",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
// maximum interoperability
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
"SSL_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA",
// additionally
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"
)
val availableCiphers = socket.supportedCipherSuites
Logger.log.info("Available cipher suites: ${availableCiphers.joinToString(", ")}")
/* For maximum security, preferredCiphers should *replace* enabled ciphers (thus
* disabling ciphers which are enabled by default, but have become unsecure), but for
* the security level of DAVdroid and maximum compatibility, disabling of insecure
* ciphers should be a server-side task */
// for the final set of enabled ciphers, take the ciphers enabled by default, ...
val _cipherSuites = LinkedList<String>()
_cipherSuites.addAll(socket.enabledCipherSuites)
Logger.log.fine("Cipher suites enabled by default: ${_cipherSuites.joinToString(", ")}")
// ... add explicitly allowed ciphers ...
_cipherSuites.addAll(knownCiphers)
// ... and keep only those which are actually available
_cipherSuites.retainAll(availableCiphers)
Logger.log.info("Enabling (only) these TLS ciphers: " + _cipherSuites.joinToString(", "))
cipherSuites = _cipherSuites.toTypedArray()
}
} catch(e: IOException) {
Logger.log.severe("Couldn't determine default TLS settings")
} finally {
socket?.close() // doesn't implement Closeable on all supported Android versions
}
}
}
}
init {
try {
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, arrayOf(trustManager), null)
delegate = sslContext.socketFactory
} catch (e: GeneralSecurityException) {
throw IllegalStateException() // system has no TLS
}
}
override fun getDefaultCipherSuites(): Array<String>? = cipherSuites ?: delegate.defaultCipherSuites
override fun getSupportedCipherSuites(): Array<String>? = cipherSuites ?: delegate.supportedCipherSuites
override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket {
val ssl = delegate.createSocket(s, host, port, autoClose)
if (ssl is SSLSocket)
upgradeTLS(ssl)
return ssl
}
override fun createSocket(host: String, port: Int): Socket {
val ssl = delegate.createSocket(host, port)
if (ssl is SSLSocket)
upgradeTLS(ssl)
return ssl
}
override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket {
val ssl = delegate.createSocket(host, port, localHost, localPort)
if (ssl is SSLSocket)
upgradeTLS(ssl)
return ssl
}
override fun createSocket(host: InetAddress, port: Int): Socket {
val ssl = delegate.createSocket(host, port)
if (ssl is SSLSocket)
upgradeTLS(ssl)
return ssl
}
override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket {
val ssl = delegate.createSocket(address, port, localAddress, localPort)
if (ssl is SSLSocket)
upgradeTLS(ssl)
return ssl
}
private fun upgradeTLS(ssl: SSLSocket) {
protocols?.let { ssl.enabledProtocols = it }
cipherSuites?.let { ssl.enabledCipherSuites = it }
}
}
@@ -0,0 +1,97 @@
/*
* 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.accounts.Account
import android.accounts.AccountManager
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import androidx.annotation.WorkerThread
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.model.AppDatabase
import at.bitfire.davdroid.model.Service
import at.bitfire.davdroid.resource.TaskUtils
import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.davdroid.settings.Settings
import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.ical4android.TaskProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class TasksWatcher(
context: Context
): PackageChangedReceiver(context) {
companion object {
@WorkerThread
fun updateTaskSync(context: Context) {
val tasksProvider = TaskUtils.currentProvider(context)
Logger.log.info("App launched or other package (un)installed; current tasks provider = $tasksProvider")
var permissionsRequired = false // whether additional permissions are required
val currentProvider by lazy { // only this provider shall be enabled (null to disable all providers)
TaskUtils.currentProvider(context)
}
// check all accounts and (de)activate task provider(s) if a CalDAV service is defined
val db = AppDatabase.getInstance(context)
val accountManager = AccountManager.get(context)
for (account in accountManager.getAccountsByType(context.getString(R.string.account_type))) {
val hasCalDAV = db.serviceDao().getByAccountAndType(account.name, Service.TYPE_CALDAV) != null
for (providerName in TaskProvider.ProviderName.values()) {
val isSyncable = ContentResolver.getIsSyncable(account, providerName.authority) // may be -1 (unknown state)
val shallBeSyncable = hasCalDAV && providerName == currentProvider
if ((shallBeSyncable && isSyncable != 1) || (!shallBeSyncable && isSyncable != 0)) {
// enable/disable sync
setSyncable(context, account, providerName.authority, shallBeSyncable)
// if sync has just been enabled: check whether additional permissions are required
if (shallBeSyncable && !PermissionUtils.havePermissions(context, providerName.permissions))
permissionsRequired = true
}
}
}
if (permissionsRequired) {
Logger.log.warning("Tasks synchronization is now enabled for at least one account, but permissions are not granted")
PermissionUtils.notifyPermissions(context, null)
}
}
private fun setSyncable(context: Context, account: Account, authority: String, syncable: Boolean) {
if (syncable) {
Logger.log.info("Enabling $authority sync for $account")
ContentResolver.setIsSyncable(account, authority, 1)
try {
val settings = AccountSettings(context, account)
val interval = settings.getSavedTasksSyncInterval() ?:
SettingsManager.getInstance(context).getLong(Settings.DEFAULT_SYNC_INTERVAL)
settings.setSyncInterval(authority, interval)
} catch (e: InvalidAccountException) {
// account has already been removed
}
} else {
Logger.log.info("Disabling $authority sync for $account")
ContentResolver.setIsSyncable(account, authority, 0)
}
}
}
override fun onReceive(context: Context, intent: Intent) {
CoroutineScope(Dispatchers.Default).launch {
updateTaskSync(context)
}
}
}
@@ -0,0 +1,79 @@
package at.bitfire.davdroid
import org.apache.commons.lang3.StringUtils
import java.util.*
class TextTable(
vararg val headers: String
) {
companion object {
fun indent(str: String, pos: Int): String =
" ".repeat(pos) +
str.split('\n').joinToString("\n" + " ".repeat(pos))
}
private val lines = mutableListOf<Array<String>>()
fun addLine(vararg values: Any?) {
if (values.size != headers.size)
throw IllegalArgumentException("Table line must have ${headers.size} column(s)")
lines += values.map {
it?.toString() ?: ""
}.toTypedArray()
}
override fun toString(): String {
val sb = StringBuilder()
val headerWidths = headers.map { it.length }
val colWidths = Array<Int>(headers.size) { colIdx ->
Collections.max(listOf(headerWidths[colIdx]) + lines.map { it[colIdx] }.map { it.length })
}
// first line
sb.append("\n")
for (colIdx in 0 until headers.size)
sb .append(StringUtils.repeat('─', colWidths[colIdx] + 2))
.append(if (colIdx == headers.size - 1) '┐' else '┬')
sb.append('\n')
// header
sb.append('│')
for (colIdx in 0 until headers.size)
sb .append(' ')
.append(StringUtils.rightPad(headers[colIdx], colWidths[colIdx] + 1))
.append('│')
sb.append('\n')
// separator between header and body
sb.append('├')
for (colIdx in 0 until headers.size) {
sb .append(StringUtils.repeat('─', colWidths[colIdx] + 2))
.append(if (colIdx == headers.size - 1) '┤' else '┼')
}
sb.append('\n')
// body
for (line in lines) {
for (colIdx in 0 until headers.size)
sb .append("")
.append(StringUtils.rightPad(line[colIdx], colWidths[colIdx] + 1))
sb.append("\n")
}
// last line
sb.append("")
for (colIdx in 0 until headers.size) {
sb .append("".repeat(colWidths[colIdx] + 2))
.append(if (colIdx == headers.size - 1) '┘' else '┴')
}
sb.append("\n\n")
return sb.toString()
}
}
@@ -18,7 +18,7 @@ import java.util.logging.LogRecord
object LogcatHandler: Handler() {
private val MAX_LINE_LENGTH = 3000
private const val MAX_LINE_LENGTH = 3000
init {
formatter = PlainTextFormatter.LOGCAT
@@ -8,89 +8,138 @@
package at.bitfire.davdroid.log
import android.content.Context
import android.app.Application
import android.app.PendingIntent
import android.content.Intent
import android.content.SharedPreferences
import android.os.Process
import android.support.v4.app.NotificationCompat
import android.support.v4.app.NotificationManagerCompat
import android.util.Log
import at.bitfire.davdroid.App
import at.bitfire.davdroid.Constants
import android.widget.Toast
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.preference.PreferenceManager
import at.bitfire.davdroid.BuildConfig
import at.bitfire.davdroid.R
import at.bitfire.davdroid.model.ServiceDB
import at.bitfire.davdroid.model.Settings
import org.apache.commons.lang3.time.DateFormatUtils
import at.bitfire.davdroid.ui.AppSettingsActivity
import at.bitfire.davdroid.ui.DebugInfoActivity
import at.bitfire.davdroid.ui.NotificationUtils
import java.io.File
import java.io.IOException
import java.util.*
import java.util.logging.FileHandler
import java.util.logging.Level
import java.util.logging.Logger
object Logger {
object Logger : SharedPreferences.OnSharedPreferenceChangeListener {
@JvmField
val log = Logger.getLogger("davdroid")!!
init {
at.bitfire.dav4android.Constants.log = Logger.getLogger("davdroid.dav4android")
at.bitfire.cert4android.Constants.log = Logger.getLogger("davdroid.cert4android")
private const val LOG_TO_FILE = "log_to_file"
val log: java.util.logging.Logger = java.util.logging.Logger.getLogger("davx5")
private lateinit var context: Application
private lateinit var preferences: SharedPreferences
fun initialize(app: Application) {
context = app
preferences = PreferenceManager.getDefaultSharedPreferences(context)
preferences.registerOnSharedPreferenceChangeListener(this)
reinitialize()
}
fun reinitLogger(context: Context) {
ServiceDB.OpenHelper(context).use { dbHelper ->
val settings = Settings(dbHelper.getReadableDatabase())
val logToFile = settings.getBoolean(App.LOG_TO_EXTERNAL_STORAGE, false)
val logVerbose = logToFile || Log.isLoggable(log.name, Log.DEBUG)
log.info("Verbose logging: $logVerbose")
// set logging level according to preferences
val rootLogger = Logger.getLogger("")
rootLogger.level = if (logVerbose) Level.ALL else Level.INFO
// remove all handlers and add our own logcat handler
rootLogger.useParentHandlers = false
rootLogger.handlers.forEach { rootLogger.removeHandler(it) }
rootLogger.addHandler(LogcatHandler)
val nm = NotificationManagerCompat.from(context)
// log to external file according to preferences
if (logToFile) {
val builder = NotificationCompat.Builder(context)
builder .setSmallIcon(R.drawable.ic_sd_storage_light)
.setLargeIcon(App.getLauncherBitmap(context))
.setContentTitle(context.getString(R.string.logging_davdroid_file_logging))
.setLocalOnly(true)
val dir = context.getExternalFilesDir(null)
if (dir != null)
try {
val fileName = File(dir, "davdroid-${Process.myPid()}-${DateFormatUtils.format(System.currentTimeMillis(), "yyyyMMdd-HHmmss")}.txt").toString()
log.info("Logging to $fileName")
val fileHandler = FileHandler(fileName)
fileHandler.formatter = PlainTextFormatter.DEFAULT
log.addHandler(fileHandler)
builder .setContentText(dir.path)
.setSubText(context.getString(R.string.logging_to_external_storage_warning))
.setCategory(NotificationCompat.CATEGORY_STATUS)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setStyle(NotificationCompat.BigTextStyle()
.bigText(context.getString(R.string.logging_to_external_storage, dir.path)))
.setOngoing(true)
} catch (e: IOException) {
log.log(Level.SEVERE, "Couldn't create external log file", e)
builder .setContentText(context.getString(R.string.logging_couldnt_create_file, e.localizedMessage))
.setCategory(NotificationCompat.CATEGORY_ERROR)
}
else
builder.setContentText(context.getString(R.string.logging_no_external_storage))
nm.notify(Constants.NOTIFICATION_EXTERNAL_FILE_LOGGING, builder.build())
} else
nm.cancel(Constants.NOTIFICATION_EXTERNAL_FILE_LOGGING)
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
if (key == LOG_TO_FILE) {
log.info("Logging settings changed; re-initializing logger")
reinitialize()
}
}
@Synchronized
private fun reinitialize() {
val logToFile = preferences.getBoolean(LOG_TO_FILE, false)
val logVerbose = logToFile || BuildConfig.DEBUG || Log.isLoggable(log.name, Log.DEBUG)
log.info("Verbose logging: $logVerbose; to file: $logToFile")
// set logging level according to preferences
val rootLogger = java.util.logging.Logger.getLogger("")
rootLogger.level = if (logVerbose) Level.ALL else Level.INFO
// reset all handlers and add our own logcat handler
rootLogger.useParentHandlers = false
rootLogger.handlers.forEach { handler ->
rootLogger.removeHandler(handler)
if (handler is FileHandler) // gracefully close previous verbose-logging FileHandlers
handler.close()
}
rootLogger.addHandler(LogcatHandler)
val nm = NotificationManagerCompat.from(context)
// log to external file according to preferences
if (logToFile) {
val logDir = debugDir() ?: return
val logFile = File(logDir, "davx5-log.txt")
if (logFile.createNewFile())
logFile.writeText("Log file created at ${Date()}; PID ${Process.myPid()}; UID ${Process.myUid()}\n")
try {
val fileHandler = FileHandler(logFile.toString(), true).apply {
formatter = PlainTextFormatter.DEFAULT
}
rootLogger.addHandler(fileHandler)
log.info("Now logging to file: $logFile")
val builder = NotificationUtils.newBuilder(context, NotificationUtils.CHANNEL_DEBUG)
builder .setSmallIcon(R.drawable.ic_sd_card_notify)
.setContentTitle(context.getString(R.string.app_settings_logging))
.setCategory(NotificationCompat.CATEGORY_STATUS)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentText(context.getString(R.string.logging_notification_text, context.getString(R.string.app_name)))
.setOngoing(true)
val shareIntent = Intent(Intent.ACTION_SEND, null, context, DebugInfoActivity::class.java)
shareIntent.putExtra(DebugInfoActivity.EXTRA_LOG_FILE, logFile.absolutePath)
shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
val pendingShare = PendingIntent.getActivity(context, 0, shareIntent, PendingIntent.FLAG_UPDATE_CURRENT)
builder.addAction(NotificationCompat.Action.Builder(
R.drawable.ic_share_action,
context.getString(R.string.logging_notification_view_share),
pendingShare
).build())
val prefIntent = Intent(context, AppSettingsActivity::class.java)
prefIntent.putExtra(AppSettingsActivity.EXTRA_SCROLL_TO, LOG_TO_FILE)
prefIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
val pendingPref = PendingIntent.getActivity(context, 0, prefIntent, PendingIntent.FLAG_UPDATE_CURRENT)
builder.addAction(NotificationCompat.Action.Builder(
R.drawable.ic_settings_action,
context.getString(R.string.logging_notification_disable),
pendingPref
).build())
nm.notify(NotificationUtils.NOTIFY_EXTERNAL_FILE_LOGGING, builder.build())
} catch(e: IOException) {
log.log(Level.SEVERE, "Couldn't create log file", e)
Toast.makeText(context, context.getString(R.string.logging_couldnt_create_file), Toast.LENGTH_LONG).show()
}
} else {
// verbose logging is disabled -> cancel notification and remove old logs
nm.cancel(NotificationUtils.NOTIFY_EXTERNAL_FILE_LOGGING)
debugDir()?.deleteRecursively()
}
}
private fun debugDir(): File? {
val dir = File(context.filesDir, "debug")
if (dir.exists() && dir.isDirectory)
return dir
if (dir.mkdir())
return dir
Toast.makeText(context, context.getString(R.string.logging_couldnt_create_file), Toast.LENGTH_LONG).show()
return null
}
}
@@ -8,34 +8,36 @@
package at.bitfire.davdroid.log
import org.apache.commons.lang3.StringUtils
import org.apache.commons.lang3.exception.ExceptionUtils
import org.apache.commons.lang3.time.DateFormatUtils
import java.util.*
import java.util.logging.Formatter
import java.util.logging.LogRecord
class PlainTextFormatter private constructor(
val logcat: Boolean
private val logcat: Boolean
): Formatter() {
companion object {
@JvmField val LOGCAT = PlainTextFormatter(true)
@JvmField val DEFAULT = PlainTextFormatter(false)
val LOGCAT = PlainTextFormatter(true)
val DEFAULT = PlainTextFormatter(false)
const val MAX_MESSAGE_LENGTH = 20000
}
override fun format(r: LogRecord): String {
val builder = StringBuilder()
if (!logcat)
builder .append(DateFormatUtils.format(r.millis, "yyyy-MM-dd HH:mm:ss"))
builder .append(DateFormatUtils.format(r.millis, "yyyy-MM-dd HH:mm:ss", Locale.ROOT))
.append(" ").append(r.threadID).append(" ")
if (r.sourceClassName.replaceFirst("\\$.*", "") != r.loggerName) {
val className = shortClassName(r.sourceClassName)
if (className != "ical4android.AndroidAppender")
builder.append("[").append(className).append("] ")
}
val className = shortClassName(r.sourceClassName)
if (className != r.loggerName)
builder.append("[").append(className).append("] ")
builder.append(r.message)
builder.append(StringUtils.abbreviate(r.message, MAX_MESSAGE_LENGTH))
r.thrown?.let {
builder .append("\nEXCEPTION ")
@@ -54,8 +56,7 @@ class PlainTextFormatter private constructor(
}
private fun shortClassName(className: String) = className
.replace("at.bitfire.davdroid.", "")
.replace("at.bitfire.", "")
.replace(Regex("^at\\.bitfire\\.(dav|cert4an|dav4an|ical4an|vcard4an)droid\\."), "")
.replace(Regex("\\$.*$"), "")
}
@@ -8,8 +8,8 @@
package at.bitfire.davdroid.log
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.Handler
import java.util.logging.LogRecord
class StringHandler: Handler() {
@@ -0,0 +1,201 @@
package at.bitfire.davdroid.model
import android.content.Context
import android.database.sqlite.SQLiteQueryBuilder
import androidx.core.database.getStringOrNull
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import at.bitfire.davdroid.AndroidSingleton
import at.bitfire.davdroid.TextTable
import at.bitfire.davdroid.log.Logger
import java.io.Writer
@Suppress("ClassName")
@Database(entities = [
Service::class,
HomeSet::class,
Collection::class
], exportSchema = true, version = 8)
@TypeConverters(Converters::class)
abstract class AppDatabase: RoomDatabase() {
abstract fun serviceDao(): ServiceDao
abstract fun homeSetDao(): HomeSetDao
abstract fun collectionDao(): CollectionDao
companion object: AndroidSingleton<AppDatabase>() {
override fun createInstance(context: Context): AppDatabase =
Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "services.db")
.addMigrations(*migrations)
.fallbackToDestructiveMigration() // as a last fallback, recreate database instead of crashing
.build()
// migrations
val migrations: Array<Migration> = arrayOf(
object : Migration(7, 8) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE homeset ADD COLUMN personal INTEGER NOT NULL DEFAULT 1")
db.execSQL("ALTER TABLE collection ADD COLUMN homeSetId INTEGER DEFAULT NULL REFERENCES homeset(id) ON DELETE SET NULL")
db.execSQL("ALTER TABLE collection ADD COLUMN owner TEXT DEFAULT NULL")
db.execSQL("CREATE INDEX index_collection_homeSetId_type ON collection(homeSetId, type)")
}
},
object : Migration(6, 7) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE homeset ADD COLUMN privBind INTEGER NOT NULL DEFAULT 1")
db.execSQL("ALTER TABLE homeset ADD COLUMN displayName TEXT DEFAULT NULL")
}
},
object : Migration(5, 6) {
override fun migrate(db: SupportSQLiteDatabase) {
val sql = arrayOf(
// migrate "services" to "service": rename columns, make id NOT NULL
"CREATE TABLE service(" +
"id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," +
"accountName TEXT NOT NULL," +
"type TEXT NOT NULL," +
"principal TEXT DEFAULT NULL" +
")",
"CREATE UNIQUE INDEX index_service_accountName_type ON service(accountName, type)",
"INSERT INTO service(id, accountName, type, principal) SELECT _id, accountName, service, principal FROM services",
"DROP TABLE services",
// migrate "homesets" to "homeset": rename columns, make id NOT NULL
"CREATE TABLE homeset(" +
"id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," +
"serviceId INTEGER NOT NULL," +
"url TEXT NOT NULL," +
"FOREIGN KEY (serviceId) REFERENCES service(id) ON DELETE CASCADE" +
")",
"CREATE UNIQUE INDEX index_homeset_serviceId_url ON homeset(serviceId, url)",
"INSERT INTO homeset(id, serviceId, url) SELECT _id, serviceID, url FROM homesets",
"DROP TABLE homesets",
// migrate "collections" to "collection": rename columns, make id NOT NULL
"CREATE TABLE collection(" +
"id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," +
"serviceId INTEGER NOT NULL," +
"type TEXT NOT NULL," +
"url TEXT NOT NULL," +
"privWriteContent INTEGER NOT NULL DEFAULT 1," +
"privUnbind INTEGER NOT NULL DEFAULT 1," +
"forceReadOnly INTEGER NOT NULL DEFAULT 0," +
"displayName TEXT DEFAULT NULL," +
"description TEXT DEFAULT NULL," +
"color INTEGER DEFAULT NULL," +
"timezone TEXT DEFAULT NULL," +
"supportsVEVENT INTEGER DEFAULT NULL," +
"supportsVTODO INTEGER DEFAULT NULL," +
"supportsVJOURNAL INTEGER DEFAULT NULL," +
"source TEXT DEFAULT NULL," +
"sync INTEGER NOT NULL DEFAULT 0," +
"FOREIGN KEY (serviceId) REFERENCES service(id) ON DELETE CASCADE" +
")",
"CREATE INDEX index_collection_serviceId_type ON collection(serviceId,type)",
"INSERT INTO collection(id, serviceId, type, url, privWriteContent, privUnbind, forceReadOnly, displayName, description, color, timezone, supportsVEVENT, supportsVTODO, source, sync) " +
"SELECT _id, serviceID, type, url, privWriteContent, privUnbind, forceReadOnly, displayName, description, color, timezone, supportsVEVENT, supportsVTODO, source, sync FROM collections",
"DROP TABLE collections"
)
sql.forEach { db.execSQL(it) }
}
},
object : Migration(4, 5) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE collections ADD COLUMN privWriteContent INTEGER DEFAULT 0 NOT NULL")
db.execSQL("UPDATE collections SET privWriteContent=NOT readOnly")
db.execSQL("ALTER TABLE collections ADD COLUMN privUnbind INTEGER DEFAULT 0 NOT NULL")
db.execSQL("UPDATE collections SET privUnbind=NOT readOnly")
// there's no DROP COLUMN in SQLite, so just keep the "readOnly" column
}
},
object : Migration(3, 4) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE collections ADD COLUMN forceReadOnly INTEGER DEFAULT 0 NOT NULL")
}
},
object : Migration(2, 3) {
override fun migrate(db: SupportSQLiteDatabase) {
// We don't have access to the context in a Room migration now, so
// we will just drop those settings from old DAVx5 versions.
Logger.log.warning("Dropping settings distrustSystemCerts and overrideProxy*")
/*val edit = PreferenceManager.getDefaultSharedPreferences(context).edit()
try {
db.query("settings", arrayOf("setting", "value"), null, null, null, null, null).use { cursor ->
while (cursor.moveToNext()) {
when (cursor.getString(0)) {
"distrustSystemCerts" -> edit.putBoolean(App.DISTRUST_SYSTEM_CERTIFICATES, cursor.getInt(1) != 0)
"overrideProxy" -> edit.putBoolean(App.OVERRIDE_PROXY, cursor.getInt(1) != 0)
"overrideProxyHost" -> edit.putString(App.OVERRIDE_PROXY_HOST, cursor.getString(1))
"overrideProxyPort" -> edit.putInt(App.OVERRIDE_PROXY_PORT, cursor.getInt(1))
StartupDialogFragment.HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED ->
edit.putBoolean(StartupDialogFragment.HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED, cursor.getInt(1) != 0)
StartupDialogFragment.HINT_OPENTASKS_NOT_INSTALLED ->
edit.putBoolean(StartupDialogFragment.HINT_OPENTASKS_NOT_INSTALLED, cursor.getInt(1) != 0)
}
}
}
db.execSQL("DROP TABLE settings")
} finally {
edit.apply()
}*/
}
},
object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE collections ADD COLUMN type TEXT NOT NULL DEFAULT ''")
db.execSQL("ALTER TABLE collections ADD COLUMN source TEXT DEFAULT NULL")
db.execSQL("UPDATE collections SET type=(" +
"SELECT CASE service WHEN ? THEN ? ELSE ? END " +
"FROM services WHERE _id=collections.serviceID" +
")",
arrayOf("caldav", "CALENDAR", "ADDRESS_BOOK"))
}
}
)
}
fun dump(writer: Writer) {
val db = openHelper.readableDatabase
db.beginTransactionNonExclusive()
// iterate through all tables
db.query(SQLiteQueryBuilder.buildQueryString(false, "sqlite_master", arrayOf("name"), "type='table'", null, null, null, null)).use { cursorTables ->
while (cursorTables.moveToNext()) {
val tableName = cursorTables.getString(0)
writer.append("$tableName\n")
db.query("SELECT * FROM $tableName").use { cursor ->
val table = TextTable(*cursor.columnNames)
val cols = cursor.columnCount
// print rows
while (cursor.moveToNext()) {
val values = Array<String?>(cols) { idx -> cursor.getStringOrNull(idx) }
table.addLine(*values)
}
writer.append(table.toString())
}
}
db.endTransaction()
}
}
}
@@ -0,0 +1,169 @@
package at.bitfire.davdroid.model
import androidx.room.*
import at.bitfire.dav4jvm.Response
import at.bitfire.dav4jvm.UrlUtils
import at.bitfire.dav4jvm.property.*
import at.bitfire.davdroid.DavUtils
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import org.apache.commons.lang3.StringUtils
@Entity(tableName = "collection",
foreignKeys = [
ForeignKey(entity = Service::class, parentColumns = arrayOf("id"), childColumns = arrayOf("serviceId"), onDelete = ForeignKey.CASCADE),
ForeignKey(entity = HomeSet::class, parentColumns = arrayOf("id"), childColumns = arrayOf("homeSetId"), onDelete = ForeignKey.SET_NULL)
],
indices = [
Index("serviceId","type"),
Index("homeSetId","type")
]
)
data class Collection(
@PrimaryKey(autoGenerate = true)
override var id: Long = 0,
var serviceId: Long = 0,
var homeSetId: Long? = null,
var type: String,
var url: HttpUrl,
var privWriteContent: Boolean = true,
var privUnbind: Boolean = true,
var forceReadOnly: Boolean = false,
var displayName: String? = null,
var description: String? = null,
var owner: HttpUrl? = null,
// CalDAV only
var color: Int? = null,
/** timezone definition (full VTIMEZONE) - not a TZID! **/
var timezone: String? = null,
/** whether the collection supports VEVENT; in case of calendars: null means true */
var supportsVEVENT: Boolean? = null,
/** whether the collection supports VTODO; in case of calendars: null means true */
var supportsVTODO: Boolean? = null,
/** whether the collection supports VJOURNAL; in case of calendars: null means true */
var supportsVJOURNAL: Boolean? = null,
/** Webcal subscription source URL */
var source: HttpUrl? = null,
/** whether this collection has been selected for synchronization */
var sync: Boolean = false
): IdEntity() {
@Ignore
var refHomeSet: HomeSet? = null
companion object {
const val TYPE_ADDRESSBOOK = "ADDRESS_BOOK"
const val TYPE_CALENDAR = "CALENDAR"
const val TYPE_WEBCAL = "WEBCAL"
/**
* Generates a collection entity from a WebDAV response.
* @param dav WebDAV response
* @return null if the response doesn't represent a collection
*/
fun fromDavResponse(dav: Response): Collection? {
val url = UrlUtils.withTrailingSlash(dav.href)
val type: String = dav[ResourceType::class.java]?.let { resourceType ->
when {
resourceType.types.contains(ResourceType.ADDRESSBOOK) -> TYPE_ADDRESSBOOK
resourceType.types.contains(ResourceType.CALENDAR) -> TYPE_CALENDAR
resourceType.types.contains(ResourceType.SUBSCRIBED) -> TYPE_WEBCAL
else -> null
}
} ?: return null
var privWriteContent = true
var privUnbind = true
dav[CurrentUserPrivilegeSet::class.java]?.let { privilegeSet ->
privWriteContent = privilegeSet.mayWriteContent
privUnbind = privilegeSet.mayUnbind
}
val displayName = StringUtils.trimToNull(dav[DisplayName::class.java]?.displayName)
val owner = dav[Owner::class.java]?.href?.let { ownerHref ->
dav.href.resolve(ownerHref)
}
var description: String? = null
var color: Int? = null
var timezone: String? = null
var supportsVEVENT: Boolean? = null
var supportsVTODO: Boolean? = null
var supportsVJOURNAL: Boolean? = null
var source: HttpUrl? = null
when (type) {
TYPE_ADDRESSBOOK -> {
dav[AddressbookDescription::class.java]?.let { description = it.description }
}
TYPE_CALENDAR, TYPE_WEBCAL -> {
dav[CalendarDescription::class.java]?.let { description = it.description }
dav[CalendarColor::class.java]?.let { color = it.color }
dav[CalendarTimezone::class.java]?.let { timezone = it.vTimeZone }
if (type == TYPE_CALENDAR) {
supportsVEVENT = true
supportsVTODO = true
supportsVJOURNAL = true
dav[SupportedCalendarComponentSet::class.java]?.let {
supportsVEVENT = it.supportsEvents
supportsVTODO = it.supportsTasks
supportsVJOURNAL = it.supportsJournal
}
} else { // Type.WEBCAL
dav[Source::class.java]?.let {
source = it.hrefs.firstOrNull()?.let { rawHref ->
val href = rawHref
.replace("^webcal://".toRegex(), "http://")
.replace("^webcals://".toRegex(), "https://")
href.toHttpUrlOrNull()
}
}
supportsVEVENT = true
}
}
}
return Collection(
type = type,
url = url,
privWriteContent = privWriteContent,
privUnbind = privUnbind,
displayName = displayName,
owner = owner,
description = description,
color = color,
timezone = timezone,
supportsVEVENT = supportsVEVENT,
supportsVTODO = supportsVTODO,
supportsVJOURNAL = supportsVJOURNAL,
source = source
)
}
}
// non-persistent properties
@Ignore
var confirmed: Boolean = false
// calculated properties
fun title() = displayName ?: DavUtils.lastSegmentOfUrl(url)
fun readOnly() = forceReadOnly || !privWriteContent
}
@@ -0,0 +1,52 @@
package at.bitfire.davdroid.model
import androidx.lifecycle.LiveData
import androidx.paging.DataSource
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
@Dao
interface CollectionDao: SyncableDao<Collection> {
@Query("SELECT * FROM collection WHERE id=:id")
fun get(id: Long): Collection?
@Query("SELECT * FROM collection WHERE serviceId=:serviceId")
fun getByService(serviceId: Long): List<Collection>
@Query("SELECT * FROM collection WHERE serviceId=:serviceId AND type=:type ORDER BY displayName, url")
fun getByServiceAndType(serviceId: Long, type: String): List<Collection>
/**
* Returns collections which
* - support VEVENT and/or VTODO (= supported calendar collections), or
* - have supportsVEVENT = supportsVTODO = null (= address books)
*/
@Query("SELECT * FROM collection WHERE serviceId=:serviceId AND type=:type " +
"AND (supportsVTODO OR supportsVEVENT OR (supportsVEVENT IS NULL AND supportsVTODO IS NULL)) ORDER BY displayName, URL")
fun pageByServiceAndType(serviceId: Long, type: String): DataSource.Factory<Int, Collection>
@Query("SELECT * FROM collection WHERE serviceId=:serviceId AND sync")
fun getByServiceAndSync(serviceId: Long): List<Collection>
@Query("SELECT collection.* FROM collection, homeset WHERE collection.serviceId=:serviceId AND type=:type AND homeSetId=homeset.id AND homeset.personal ORDER BY collection.displayName, collection.url")
fun pagePersonalByServiceAndType(serviceId: Long, type: String): DataSource.Factory<Int, Collection>
@Query("SELECT COUNT(*) FROM collection WHERE serviceId=:serviceId AND sync")
fun observeHasSyncByService(serviceId: Long): LiveData<Boolean>
@Query("SELECT * FROM collection WHERE serviceId=:serviceId AND type='${Collection.TYPE_CALENDAR}' AND supportsVEVENT AND sync ORDER BY displayName, url")
fun getSyncCalendars(serviceId: Long): List<Collection>
@Query("SELECT * FROM collection WHERE serviceId=:serviceId AND type='${Collection.TYPE_CALENDAR}' AND supportsVTODO AND sync ORDER BY displayName, url")
fun getSyncTaskLists(serviceId: Long): List<Collection>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertOrReplace(collection: Collection)
@Insert
fun insert(collection: Collection)
}
@@ -1,140 +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 at.bitfire.dav4android.DavResource
import at.bitfire.dav4android.property.*
import at.bitfire.davdroid.model.ServiceDB.Collections
import java.io.Serializable
data class CollectionInfo @JvmOverloads constructor(
val url: String,
var id: Long? = null,
var serviceID: Long? = null,
var type: Type? = null,
var readOnly: Boolean = false,
var displayName: String? = null,
var description: String? = null,
var color: Int? = null,
var timeZone: String? = null,
var supportsVEVENT: Boolean = false,
var supportsVTODO: Boolean = false,
var selected: Boolean = false,
// non-persistent properties
var confirmed: Boolean = false
): Serializable {
enum class Type {
ADDRESS_BOOK,
CALENDAR
}
companion object {
@JvmField
val DAV_PROPERTIES = arrayOf(
ResourceType.NAME,
CurrentUserPrivilegeSet.NAME,
DisplayName.NAME,
AddressbookDescription.NAME, SupportedAddressData.NAME,
CalendarDescription.NAME, CalendarColor.NAME, SupportedCalendarComponentSet.NAME
)
}
constructor(dav: DavResource): this(dav.location.toString()) {
(dav.properties[ResourceType.NAME] as ResourceType?)?.let { type ->
when {
type.types.contains(ResourceType.ADDRESSBOOK) -> this.type = Type.ADDRESS_BOOK
type.types.contains(ResourceType.CALENDAR) -> this.type = Type.CALENDAR
}
}
(dav.properties[CurrentUserPrivilegeSet.NAME] as CurrentUserPrivilegeSet?)?.let { privilegeSet ->
readOnly = !privilegeSet.mayWriteContent
}
(dav.properties[DisplayName.NAME] as DisplayName?)?.let {
if (!it.displayName.isNullOrEmpty())
displayName = it.displayName
}
when (type) {
Type.ADDRESS_BOOK -> {
(dav.properties[AddressbookDescription.NAME] as AddressbookDescription?)?.let { description = it.description }
}
Type.CALENDAR -> {
(dav.properties[CalendarDescription.NAME] as CalendarDescription?)?.let { description = it.description }
(dav.properties[CalendarColor.NAME] as CalendarColor?)?.let { color = it.color }
(dav.properties[CalendarTimezone.NAME] as CalendarTimezone?)?.let { timeZone = it.vTimeZone }
supportsVEVENT = true
supportsVTODO = true
(dav.properties[SupportedCalendarComponentSet.NAME] as SupportedCalendarComponentSet?)?.let {
supportsVEVENT = it.supportsEvents
supportsVTODO = it.supportsTasks
}
}
}
}
constructor(values: ContentValues): this(values.getAsString(Collections.URL)) {
id = values.getAsLong(Collections.ID)
serviceID = values.getAsLong(Collections.SERVICE_ID)
readOnly = values.getAsInteger(Collections.READ_ONLY) != 0
displayName = values.getAsString(Collections.DISPLAY_NAME)
description = values.getAsString(Collections.DESCRIPTION)
color = values.getAsInteger(Collections.COLOR)
timeZone = values.getAsString(Collections.TIME_ZONE)
supportsVEVENT = getAsBooleanOrNull(values, Collections.SUPPORTS_VEVENT) ?: false
supportsVTODO = getAsBooleanOrNull(values, Collections.SUPPORTS_VTODO) ?: false
selected = values.getAsInteger(Collections.SYNC) != 0
}
fun toDB(): ContentValues {
val values = ContentValues()
// Collections.SERVICE_ID is never changed
values.put(Collections.URL, url)
values.put(Collections.READ_ONLY, if (readOnly) 1 else 0)
values.put(Collections.DISPLAY_NAME, displayName)
values.put(Collections.DESCRIPTION, description)
values.put(Collections.COLOR, color)
values.put(Collections.TIME_ZONE, timeZone)
values.put(Collections.SUPPORTS_VEVENT, if (supportsVEVENT) 1 else 0)
values.put(Collections.SUPPORTS_VTODO, if (supportsVTODO) 1 else 0)
values.put(Collections.SYNC, if (selected) 1 else 0)
return values
}
private fun getAsBooleanOrNull(values: ContentValues, field: String): Boolean? {
val i = values.getAsInteger(field)
return if (i == null)
null
else
(i != 0)
}
}
@@ -0,0 +1,17 @@
package at.bitfire.davdroid.model
import androidx.room.TypeConverter
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
class Converters {
@TypeConverter
fun httpUrlToString(url: HttpUrl?) =
url?.toString()
@TypeConverter
fun stringToHttpUrl(url: String?): HttpUrl? =
url?.toHttpUrlOrNull()
}
@@ -0,0 +1,22 @@
/*
* 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
class Credentials(
val userName: String? = null,
val password: String? = null,
val certificateAlias: String? = null
) {
override fun toString(): String {
val maskedPassword = "*****".takeIf { password != null }
return "Credentials(userName=$userName, password=$maskedPassword, certificateAlias=$certificateAlias)"
}
}
@@ -0,0 +1,46 @@
package at.bitfire.davdroid.model
import at.bitfire.davdroid.log.Logger
import java.util.logging.Level
class DaoTools<T: IdEntity>(dao: SyncableDao<T>): SyncableDao<T> by dao {
/**
* Synchronizes a list of "old" elements with a list of "new" elements so that the list
* only contain equal elements.
*
* @param allOld list of old elements
* @param allNew map of new elements (stored in key map)
* @param selectKey generates a unique key from the element (will be called on old elements)
* @param prepareNew prepares new elements (can be used to take over properties of old elements)
*/
fun <K> syncAll(allOld: List<T>, allNew: Map<K,T>, selectKey: (T) -> K, prepareNew: (new: T, old: T) -> Unit = { _, _ -> }) {
Logger.log.log(Level.FINE, "Syncing tables", arrayOf(allOld, allNew))
val remainingNew = allNew.toMutableMap()
allOld.forEach { old ->
val key = selectKey(old)
val matchingNew = remainingNew[key]
if (matchingNew != null) {
// keep this old item, but maybe update it
matchingNew.id = old.id // identity is proven by key
prepareNew(matchingNew, old)
if (matchingNew != old)
update(matchingNew)
// remove from remainingNew
remainingNew -= key
} else {
// this old item is not present anymore, delete it
delete(old)
}
}
val toInsert = remainingNew.values.toList()
val insertIds = insert(toInsert)
insertIds.withIndex().forEach { (idx, id) ->
toInsert[idx].id = id
}
}
}
@@ -0,0 +1,34 @@
package at.bitfire.davdroid.model
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.PrimaryKey
import okhttp3.HttpUrl
@Entity(tableName = "homeset",
foreignKeys = [
ForeignKey(entity = Service::class, parentColumns = arrayOf("id"), childColumns = arrayOf("serviceId"), onDelete = ForeignKey.CASCADE)
],
indices = [
// index by service; no duplicate URLs per service
Index("serviceId", "url", unique = true)
]
)
data class HomeSet(
@PrimaryKey(autoGenerate = true)
override var id: Long,
var serviceId: Long,
/**
* Whether this homeset belongs to the [Service.principal] given by [serviceId].
*/
var personal: Boolean,
var url: HttpUrl,
var privBind: Boolean = true,
var displayName: String? = null
): IdEntity()
@@ -0,0 +1,24 @@
package at.bitfire.davdroid.model
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
@Dao
interface HomeSetDao: SyncableDao<HomeSet> {
@Query("SELECT * FROM homeset WHERE serviceId=:serviceId")
fun getByService(serviceId: Long): List<HomeSet>
@Query("SELECT * FROM homeset WHERE serviceId=:serviceId AND privBind")
fun getBindableByService(serviceId: Long): List<HomeSet>
@Query("SELECT COUNT(*) FROM homeset WHERE serviceId=:serviceId AND privBind")
fun hasBindableByService(serviceId: Long): LiveData<Boolean>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertOrReplace(homeSet: HomeSet): Long
}
@@ -0,0 +1,5 @@
package at.bitfire.davdroid.model
abstract class IdEntity {
abstract var id: Long
}
@@ -0,0 +1,28 @@
package at.bitfire.davdroid.model
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import okhttp3.HttpUrl
@Entity(tableName = "service",
indices = [
// only one service per type and account
Index("accountName", "type", unique = true)
])
data class Service(
@PrimaryKey(autoGenerate = true)
var id: Long,
var accountName: String,
var type: String,
var principal: HttpUrl?
) {
companion object {
const val TYPE_CALDAV = "caldav"
const val TYPE_CARDDAV = "carddav"
}
}
@@ -1,176 +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.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteException
import android.database.sqlite.SQLiteOpenHelper
import at.bitfire.davdroid.log.Logger
import java.io.Closeable
class ServiceDB {
object Settings {
@JvmField val _TABLE = "settings"
@JvmField val NAME = "setting"
@JvmField val VALUE = "value"
}
object Services {
@JvmField val _TABLE = "services"
@JvmField val ID = "_id"
@JvmField val ACCOUNT_NAME = "accountName"
@JvmField val SERVICE = "service"
@JvmField val PRINCIPAL = "principal"
// allowed values for SERVICE column
@JvmField val SERVICE_CALDAV = "caldav"
@JvmField val SERVICE_CARDDAV = "carddav"
}
object HomeSets {
@JvmField val _TABLE = "homesets"
@JvmField val ID = "_id"
@JvmField val SERVICE_ID = "serviceID"
@JvmField val URL = "url"
}
object Collections {
@JvmField val _TABLE = "collections"
@JvmField val ID = "_id"
@JvmField val SERVICE_ID = "serviceID"
@JvmField val URL = "url"
@JvmField val READ_ONLY = "readOnly"
@JvmField val DISPLAY_NAME = "displayName"
@JvmField val DESCRIPTION = "description"
@JvmField val COLOR = "color"
@JvmField val TIME_ZONE = "timezone"
@JvmField val SUPPORTS_VEVENT = "supportsVEVENT"
@JvmField val SUPPORTS_VTODO = "supportsVTODO"
@JvmField val SYNC = "sync"
}
companion object {
@JvmStatic
fun onRenameAccount(db: SQLiteDatabase, oldName: String, newName: String) {
val values = ContentValues(1)
values.put(Services.ACCOUNT_NAME, newName)
db.update(Services._TABLE, values, Services.ACCOUNT_NAME + "=?", arrayOf(oldName))
}
}
class OpenHelper(
context: Context
): SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION), Closeable {
companion object {
val DATABASE_NAME = "services.db"
val DATABASE_VERSION = 1
}
override fun onConfigure(db: SQLiteDatabase) {
setWriteAheadLoggingEnabled(true)
db.setForeignKeyConstraintsEnabled(true)
}
override fun onCreate(db: SQLiteDatabase) {
Logger.log.info("Creating database " + db.path)
db.execSQL("CREATE TABLE ${Settings._TABLE}(" +
"${Settings.NAME} TEXT NOT NULL," +
"${Settings.VALUE} TEXT NOT NULL)")
db.execSQL("CREATE UNIQUE INDEX settings_name ON ${Settings._TABLE} (${Settings.NAME})")
db.execSQL("CREATE TABLE ${Services._TABLE}(" +
"${Services.ID} INTEGER PRIMARY KEY AUTOINCREMENT," +
"${Services.ACCOUNT_NAME} TEXT NOT NULL," +
"${Services.SERVICE} TEXT NOT NULL," +
"${Services.PRINCIPAL} TEXT NULL)")
db.execSQL("CREATE UNIQUE INDEX services_account ON ${Services._TABLE} (${Services.ACCOUNT_NAME},${Services.SERVICE})")
db.execSQL("CREATE TABLE ${HomeSets._TABLE}(" +
"${HomeSets.ID} INTEGER PRIMARY KEY AUTOINCREMENT," +
"${HomeSets.SERVICE_ID} INTEGER NOT NULL REFERENCES ${Services._TABLE} ON DELETE CASCADE," +
"${HomeSets.URL} TEXT NOT NULL)")
db.execSQL("CREATE UNIQUE INDEX homesets_service_url ON ${HomeSets._TABLE}(${HomeSets.SERVICE_ID},${HomeSets.URL})")
db.execSQL("CREATE TABLE ${Collections._TABLE}(" +
"${Collections.ID} INTEGER PRIMARY KEY AUTOINCREMENT," +
"${Collections.SERVICE_ID} INTEGER NOT NULL REFERENCES ${Services._TABLE} ON DELETE CASCADE," +
"${Collections.URL} TEXT NOT NULL," +
"${Collections.READ_ONLY} INTEGER DEFAULT 0 NOT NULL," +
"${Collections.DISPLAY_NAME} TEXT NULL," +
"${Collections.DESCRIPTION} TEXT NULL," +
"${Collections.COLOR} INTEGER NULL," +
"${Collections.TIME_ZONE} TEXT NULL," +
"${Collections.SUPPORTS_VEVENT} INTEGER NULL," +
"${Collections.SUPPORTS_VTODO} INTEGER NULL," +
"${Collections.SYNC} INTEGER DEFAULT 0 NOT NULL)")
db.execSQL("CREATE UNIQUE INDEX collections_service_url ON ${Collections._TABLE}(${Collections.SERVICE_ID},${Collections.URL})")
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
// no different versions yet
}
fun dump(sb: StringBuilder) {
val db = readableDatabase
db.beginTransactionNonExclusive()
// iterate through all tables
db.query("sqlite_master", arrayOf("name"), "type='table'", null, null, null, null).use { cursorTables ->
while (cursorTables.moveToNext()) {
val table = cursorTables.getString(0)
sb.append(table).append("\n")
db.query(table, null, null, null, null, null, null).use { cursor ->
// print columns
val cols = cursor.columnCount
sb.append("\t| ")
for (i in 0 .. cols-1)
sb .append(" ")
.append(cursor.getColumnName(i))
.append(" |")
sb.append("\n")
// print rows
while (cursor.moveToNext()) {
sb.append("\t| ")
for (i in 0 .. cols-1) {
sb.append(" ")
try {
val value = cursor.getString(i)
if (value != null)
sb.append(value
.replace("\r", "<CR>")
.replace("\n", "<LF>"))
else
sb.append("<null>")
} catch (e: SQLiteException) {
sb.append("<unprintable>")
}
sb.append(" |")
}
sb.append("\n")
}
sb.append("----------\n")
}
}
db.endTransaction()
}
}
}
}
@@ -0,0 +1,33 @@
package at.bitfire.davdroid.model
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
@Dao
interface ServiceDao {
@Query("SELECT * FROM service WHERE accountName=:accountName AND type=:type")
fun getByAccountAndType(accountName: String, type: String): Service?
@Query("SELECT id FROM service WHERE accountName=:accountName AND type=:type")
fun getIdByAccountAndType(accountName: String, type: String): LiveData<Long>
@Query("SELECT * FROM service WHERE id=:id")
fun get(id: Long): Service?
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertOrReplace(service: Service): Long
@Query("DELETE FROM service")
fun deleteAll()
@Query("DELETE FROM service WHERE accountName NOT IN (:accountNames)")
fun deleteExceptAccounts(accountNames: Array<String>)
@Query("UPDATE service SET accountName=:newName WHERE accountName=:oldName")
fun renameAccount(oldName: String, newName: String)
}
@@ -1,94 +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.BroadcastReceiver
import android.content.ContentValues;
import android.content.Context
import android.content.Intent
import android.database.sqlite.SQLiteDatabase
import at.bitfire.davdroid.CustomCertificates
import at.bitfire.davdroid.log.Logger
class Settings(
val db: SQLiteDatabase
) {
fun getBoolean(name: String, defaultValue: Boolean): Boolean {
db.query(ServiceDB.Settings._TABLE, arrayOf(ServiceDB.Settings.VALUE),
"${ServiceDB.Settings.NAME}=?", arrayOf(name), null, null, null)?.use { cursor ->
if (cursor.moveToNext() && !cursor.isNull(0))
return cursor.getInt(0) != 0
}
return defaultValue
}
fun putBoolean(name: String, value: Boolean) {
val values = ContentValues(2)
values.put(ServiceDB.Settings.NAME, name)
values.put(ServiceDB.Settings.VALUE, if (value) 1 else 0)
db.insertWithOnConflict(ServiceDB.Settings._TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE)
}
fun getInt(name: String, defaultValue: Int): Int {
db.query(ServiceDB.Settings._TABLE, arrayOf(ServiceDB.Settings.VALUE),
"${ServiceDB.Settings.NAME}=?", arrayOf(name), null, null, null)?.use { cursor ->
if (cursor.moveToNext() && !cursor.isNull(0))
return if (cursor.isNull(0)) defaultValue else cursor.getInt(0)
}
return defaultValue
}
fun putInt(name: String, value: Int?) {
val values = ContentValues(2)
values.put(ServiceDB.Settings.NAME, name)
values.put(ServiceDB.Settings.VALUE, value)
db.insertWithOnConflict(ServiceDB.Settings._TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE)
}
fun getString(name: String, defaultValue: String?): String? {
db.query(ServiceDB.Settings._TABLE, arrayOf(ServiceDB.Settings.VALUE),
"${ServiceDB.Settings.NAME}=?", arrayOf(name), null, null, null)?.use { cursor ->
if (cursor.moveToNext())
return cursor.getString(0)
}
return defaultValue
}
fun putString(name: String, value: String?) {
val values = ContentValues(2)
values.put(ServiceDB.Settings.NAME, name)
values.put(ServiceDB.Settings.VALUE, value)
db.insertWithOnConflict(ServiceDB.Settings._TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE)
}
fun remove(name: String) {
db.delete(ServiceDB.Settings._TABLE, "${ServiceDB.Settings.NAME}=?", arrayOf(name))
}
class ReinitSettingsReceiver: BroadcastReceiver() {
companion object {
@JvmField val ACTION_REINIT_SETTINGS = "at.bitfire.davdroid.REINIT_SETTINGS"
}
override fun onReceive(context: Context, intent: Intent) {
Logger.log.info("Received broadcast: re-initializing settings (logger/cert manager)")
CustomCertificates.reinitCertManager(context)
Logger.reinitLogger(context)
}
}
}
@@ -0,0 +1,63 @@
/*
* Copyright © Ricki Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
package at.bitfire.davdroid.model
import at.bitfire.dav4jvm.property.SyncToken
import org.json.JSONException
import org.json.JSONObject
data class SyncState(
val type: Type,
val value: String,
/**
* Whether this sync state occurred during an initial sync as described
* in RFC 6578, which means the initial sync is not complete yet.
*/
var initialSync: Boolean? = null
) {
companion object {
private const val KEY_TYPE = "type"
private const val KEY_VALUE = "value"
private const val KEY_INITIAL_SYNC = "initialSync"
fun fromString(s: String?): SyncState? {
if (s == null)
return null
return try {
val json = JSONObject(s)
SyncState(
Type.valueOf(json.getString(KEY_TYPE)),
json.getString(KEY_VALUE),
try { json.getBoolean(KEY_INITIAL_SYNC) } catch(e: JSONException) { null }
)
} catch (e: JSONException) {
null
}
}
fun fromSyncToken(token: SyncToken, initialSync: Boolean? = null) =
SyncState(Type.SYNC_TOKEN, requireNotNull(token.token), initialSync)
}
enum class Type { CTAG, SYNC_TOKEN }
override fun toString(): String {
val json = JSONObject()
json.put(KEY_TYPE, type.name)
json.put(KEY_VALUE, value)
initialSync?.let { json.put(KEY_INITIAL_SYNC, it) }
return json.toString()
}
}
@@ -0,0 +1,19 @@
package at.bitfire.davdroid.model
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Update
import java.util.*
interface SyncableDao<T: IdEntity> {
@Insert
fun insert(items: List<T>): LongArray
@Update
fun update(item: T)
@Delete
fun delete(item: T)
}
@@ -6,23 +6,16 @@
* http://www.gnu.org/licenses/gpl.html
*/
package at.bitfire.davdroid.model;
package at.bitfire.davdroid.model
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.RawContacts
object UnknownProperties {
@JvmField
val CONTENT_ITEM_TYPE = "x.davdroid/unknown-properties"
const val CONTENT_ITEM_TYPE = "x.davdroid/unknown-properties"
@JvmField
val MIMETYPE = RawContacts.Data.MIMETYPE
@JvmField
val RAW_CONTACT_ID = RawContacts.Data.RAW_CONTACT_ID
@JvmField
val UNKNOWN_PROPERTIES = RawContacts.Data.DATA1
const val MIMETYPE = RawContacts.Data.MIMETYPE
const val RAW_CONTACT_ID = RawContacts.Data.RAW_CONTACT_ID
const val UNKNOWN_PROPERTIES = RawContacts.Data.DATA1
}
@@ -0,0 +1,17 @@
/*
* 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.resource
import at.bitfire.vcard4android.Contact
interface LocalAddress: LocalResource<Contact> {
fun resetDeleted()
}
@@ -18,73 +18,84 @@ import android.provider.ContactsContract.CommonDataKinds.GroupMembership
import android.provider.ContactsContract.Groups
import android.provider.ContactsContract.RawContacts
import android.util.Base64
import at.bitfire.davdroid.App
import at.bitfire.davdroid.DavUtils
import at.bitfire.davdroid.R
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.model.CollectionInfo
import at.bitfire.davdroid.model.Collection
import at.bitfire.davdroid.model.SyncState
import at.bitfire.davdroid.syncadapter.AccountUtils
import at.bitfire.vcard4android.*
import java.io.ByteArrayOutputStream
import java.io.FileNotFoundException
import java.util.*
import java.util.logging.Level
/**
* A local address book. Requires an own Android account, because Android manages contacts per
* account and there is no such thing as "address books". So, DAVx5 creates a "DAVx5
* address book" account for every CardDAV address book. These accounts are bound to a
* DAVx5 main account.
*/
class LocalAddressBook(
private val context: Context,
account: Account,
provider: ContentProviderClient?
): AndroidAddressBook<LocalContact, LocalGroup>(account, provider, LocalContact.Factory, LocalGroup.Factory), LocalCollection<LocalResource> {
): AndroidAddressBook<LocalContact, LocalGroup>(account, provider, LocalContact.Factory, LocalGroup.Factory), LocalCollection<LocalAddress> {
companion object {
val USER_DATA_MAIN_ACCOUNT_TYPE = "real_account_type"
val USER_DATA_MAIN_ACCOUNT_NAME = "real_account_name"
val USER_DATA_URL = "url"
val USER_DATA_CTAG = "ctag"
const val USER_DATA_MAIN_ACCOUNT_TYPE = "real_account_type"
const val USER_DATA_MAIN_ACCOUNT_NAME = "real_account_name"
const val USER_DATA_URL = "url"
const val USER_DATA_READ_ONLY = "read_only"
@JvmStatic
@Throws(ContactsStorageException::class)
fun create(context: Context, provider: ContentProviderClient, mainAccount: Account, info: CollectionInfo): LocalAddressBook {
val accountManager = AccountManager.get(context)
val account = Account(accountName(mainAccount, info), App.addressBookAccountType)
if (!accountManager.addAccountExplicitly(account, null, initialUserData(mainAccount, info.url)))
throw ContactsStorageException("Couldn't create address book account")
fun create(context: Context, provider: ContentProviderClient, mainAccount: Account, info: Collection): LocalAddressBook {
val account = Account(accountName(mainAccount, info), context.getString(R.string.account_type_address_book))
val userData = initialUserData(mainAccount, info.url.toString())
Logger.log.log(Level.INFO, "Creating local address book $account", userData)
if (!AccountUtils.createAccount(context, account, userData))
throw IllegalStateException("Couldn't create address book account")
val addressBook = LocalAddressBook(context, account, provider)
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1)
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true)
// initialize Contacts Provider Settings
val values = ContentValues(2)
values.put(ContactsContract.Settings.SHOULD_SYNC, 1)
values.put(ContactsContract.Settings.UNGROUPED_VISIBLE, 1)
addressBook.settings = values
addressBook.readOnly = !info.privWriteContent || info.forceReadOnly
return addressBook
}
@JvmStatic
@Throws(ContactsStorageException::class)
fun find(context: Context, provider: ContentProviderClient, mainAccount: Account?): List<LocalAddressBook> {
val accountManager = AccountManager.get(context)
fun findAll(context: Context, provider: ContentProviderClient?, mainAccount: Account) = AccountManager.get(context)
.getAccountsByType(context.getString(R.string.account_type_address_book))
.map { LocalAddressBook(context, it, provider) }
.filter {
try {
it.mainAccount == mainAccount
} catch(e: IllegalStateException) {
false
}
}
.toList()
val result = LinkedList<LocalAddressBook>()
accountManager.getAccountsByType(App.addressBookAccountType)
.map { LocalAddressBook(context, it, provider) }
.filter { mainAccount == null || it.getMainAccount() == mainAccount }
.forEach { result += it }
return result
}
@JvmStatic
fun accountName(mainAccount: Account, info: CollectionInfo): String {
fun accountName(mainAccount: Account, info: Collection): String {
val baos = ByteArrayOutputStream()
baos.write(info.url.hashCode())
val hash = Base64.encodeToString(baos.toByteArray(), Base64.NO_WRAP or Base64.NO_PADDING)
val sb = StringBuilder(if (info.displayName.isNullOrEmpty()) DavUtils.lastSegmentOfUrl(info.url) else info.displayName)
sb .append(" (")
.append(mainAccount.name)
.append(" ")
.append(hash)
.append(")")
val sb = StringBuilder(info.displayName.let {
if (it.isNullOrEmpty())
DavUtils.lastSegmentOfUrl(info.url)
else
it
})
sb.append(" (${mainAccount.name} $hash)")
return sb.toString()
}
@JvmStatic
fun initialUserData(mainAccount: Account, url: String): Bundle {
val bundle = Bundle(3)
bundle.putString(USER_DATA_MAIN_ACCOUNT_NAME, mainAccount.name)
@@ -93,177 +104,239 @@ class LocalAddressBook(
return bundle
}
fun mainAccount(context: Context, account: Account): Account =
if (account.type == context.getString(R.string.account_type_address_book)) {
val manager = AccountManager.get(context)
Account(
manager.getUserData(account, USER_DATA_MAIN_ACCOUNT_NAME),
manager.getUserData(account, USER_DATA_MAIN_ACCOUNT_TYPE)
)
} else
account
}
override val tag: String
get() = "contacts-${account.name}"
override val title = account.name!!
/**
* Whether contact groups (LocalGroup resources) are included in query results for
* {@link #getAll()}, {@link #getDeleted()}, {@link #getDirty()} and
* {@link #getWithoutFileName()}.
* Whether contact groups ([LocalGroup]) are included in query results
* and are affected by updates/deletes on generic members.
*
* For instance, if this option is disabled, [findDirty] will find only dirty [LocalContact]s,
* but if it is enabled, [findDirty] will find dirty [LocalContact]s and [LocalGroup]s.
*/
var includeGroups = true
private var _mainAccount: Account? = null
/**
* The associated main account which this address book accounts belongs to.
* @throws IllegalStateException when no main account is assigned
*/
var mainAccount: Account
get() {
_mainAccount?.let { return it }
AccountManager.get(context).let { accountManager ->
val name = accountManager.getUserData(account, USER_DATA_MAIN_ACCOUNT_NAME)
val type = accountManager.getUserData(account, USER_DATA_MAIN_ACCOUNT_TYPE)
if (name != null && type != null)
return Account(name, type)
else
throw IllegalStateException("No main account assigned to address book account")
}
}
set(newMainAccount) {
AccountManager.get(context).let { accountManager ->
accountManager.setUserData(account, USER_DATA_MAIN_ACCOUNT_NAME, newMainAccount.name)
accountManager.setUserData(account, USER_DATA_MAIN_ACCOUNT_TYPE, newMainAccount.type)
}
_mainAccount = newMainAccount
}
var url: String
get() = AccountManager.get(context).getUserData(account, USER_DATA_URL)
?: throw IllegalStateException("Address book has no URL")
set(url) = AccountManager.get(context).setUserData(account, USER_DATA_URL, url)
override var readOnly: Boolean
get() = AccountManager.get(context).getUserData(account, USER_DATA_READ_ONLY) != null
set(readOnly) = AccountManager.get(context).setUserData(account, USER_DATA_READ_ONLY, if (readOnly) "1" else null)
override var lastSyncState: SyncState?
get() = syncState?.let { SyncState.fromString(String(it)) }
set(state) {
syncState = state?.toString()?.toByteArray()
}
/* operations on the collection (address book) itself */
@Throws(ContactsStorageException::class)
fun update(info: CollectionInfo) {
val newAccountName = accountName(getMainAccount(), info)
override fun markNotDirty(flags: Int): Int {
val values = ContentValues(1)
values.put(LocalContact.COLUMN_FLAGS, flags)
var number = provider!!.update(rawContactsSyncUri(), values, "${RawContacts.DIRTY}=0", null)
if (includeGroups) {
values.clear()
values.put(LocalGroup.COLUMN_FLAGS, flags)
number += provider.update(groupsSyncUri(), values, "NOT ${Groups.DIRTY}", null)
}
return number
}
override fun removeNotDirtyMarked(flags: Int): Int {
var number = provider!!.delete(rawContactsSyncUri(),
"NOT ${RawContacts.DIRTY} AND ${LocalContact.COLUMN_FLAGS}=?", arrayOf(flags.toString()))
if (includeGroups)
number += provider.delete(groupsSyncUri(),
"NOT ${Groups.DIRTY} AND ${LocalGroup.COLUMN_FLAGS}=?", arrayOf(flags.toString()))
return number
}
fun update(info: Collection) {
val newAccountName = accountName(mainAccount, info)
if (account.name != newAccountName && Build.VERSION.SDK_INT >= 21) {
// no need to re-assign contacts to new account, because they will be deleted by contacts provider in any case
val accountManager = AccountManager.get(context)
val future = accountManager.renameAccount(account, newAccountName, {
try {
// update raw contacts to new account name
provider?.let { provider ->
val values = ContentValues(1)
values.put(RawContacts.ACCOUNT_NAME, newAccountName)
provider.update(syncAdapterURI(RawContacts.CONTENT_URI), values, "${RawContacts.ACCOUNT_NAME}=?", arrayOf(account.name))
}
} catch(e: RemoteException) {
Logger.log.log(Level.WARNING, "Couldn't re-assign contacts to new account name", e)
}
}, null)
val future = accountManager.renameAccount(account, newAccountName, null, null)
account = future.result
}
val nowReadOnly = !info.privWriteContent || info.forceReadOnly
if (nowReadOnly != readOnly) {
Constants.log.info("Address book now read-only = $nowReadOnly, updating contacts")
// update address book itself
readOnly = nowReadOnly
// update raw contacts
val rawContactValues = ContentValues(1)
rawContactValues.put(RawContacts.RAW_CONTACT_IS_READ_ONLY, if (nowReadOnly) 1 else 0)
provider!!.update(rawContactsSyncUri(), rawContactValues, null, null)
// update data rows
val dataValues = ContentValues(1)
dataValues.put(ContactsContract.Data.IS_READ_ONLY, if (nowReadOnly) 1 else 0)
provider.update(syncAdapterURI(ContactsContract.Data.CONTENT_URI), dataValues, null, null)
}
// make sure it will still be synchronized when contacts are updated
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true)
if (ContentResolver.getIsSyncable(account, ContactsContract.AUTHORITY) <= 0)
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1)
if (!ContentResolver.getSyncAutomatically(account, ContactsContract.AUTHORITY))
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true)
}
@Throws(ContactsStorageException::class)
fun delete() {
val accountManager = AccountManager.get(context)
try {
if (Build.VERSION.SDK_INT >= 22)
accountManager.removeAccount(account, null, null, null)
else
@Suppress("deprecation")
accountManager.removeAccount(account, null, null)
} catch(e: Exception) {
throw ContactsStorageException("Couldn't remove address book", e)
}
@Suppress("DEPRECATION")
if (Build.VERSION.SDK_INT >= 22)
accountManager.removeAccount(account, null, null, null)
else
accountManager.removeAccount(account, null, null)
}
/* operations on members (contacts/groups) */
@Throws(ContactsStorageException::class, FileNotFoundException::class)
fun findContactByUID(uid: String): LocalContact {
val contacts = queryContacts("${AndroidContact.COLUMN_UID}=?", arrayOf(uid))
if (contacts.isEmpty())
throw FileNotFoundException()
return contacts.first()
override fun findByName(name: String): LocalAddress? {
val result = queryContacts("${AndroidContact.COLUMN_FILENAME}=?", arrayOf(name)).firstOrNull()
return if (includeGroups)
result ?: queryGroups("${AndroidGroup.COLUMN_FILENAME}=?", arrayOf(name)).firstOrNull()
else
result
}
@Throws(ContactsStorageException::class)
override fun getAll(): List<LocalResource> {
val all = LinkedList<LocalResource>()
all.addAll(queryContacts(null, null))
if (includeGroups)
all.addAll(queryGroups(null, null))
return all
}
/**
* Returns an array of local contacts/groups which have been deleted locally. (DELETED != 0).
* @throws RemoteException on content provider errors
*/
@Throws(ContactsStorageException::class)
override fun getDeleted(): List<LocalResource> {
val deleted = LinkedList<LocalResource>()
deleted.addAll(getDeletedContacts())
if (includeGroups)
deleted.addAll(getDeletedGroups())
return deleted
override fun findDeleted() =
if (includeGroups)
findDeletedContacts() + findDeletedGroups()
else
findDeletedContacts()
fun findDeletedContacts() = queryContacts(RawContacts.DELETED, null)
fun findDeletedGroups() = queryGroups(Groups.DELETED, null)
/**
* Returns an array of local contacts/groups which have been changed locally (DIRTY != 0).
* @throws RemoteException on content provider errors
*/
override fun findDirty() =
if (includeGroups)
findDirtyContacts() + findDirtyGroups()
else
findDirtyContacts()
fun findDirtyContacts() = queryContacts(RawContacts.DIRTY, null)
fun findDirtyGroups() = queryGroups(Groups.DIRTY, null)
override fun forgetETags() {
if (includeGroups) {
val values = ContentValues(1)
values.putNull(AndroidGroup.COLUMN_ETAG)
provider!!.update(groupsSyncUri(), values, null, null)
}
val values = ContentValues(1)
values.putNull(AndroidContact.COLUMN_ETAG)
provider!!.update(rawContactsSyncUri(), values, null, null)
}
/**
* Queries all contacts with DIRTY flag and checks whether their data checksum has changed, i.e.
* if they're "really dirty" (= data has changed, not only metadata, which is not hashed).
* The DIRTY flag is removed from contacts which are not "really dirty", i.e. from contacts
* whose contact data checksum has not changed.
* @return number of "really dirty" contacts
* @throws RemoteException on content provider errors
*/
@Throws(ContactsStorageException::class)
fun verifyDirty(): Int {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N || Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
throw IllegalStateException("verifyDirty() should not be called on Android != 7")
var reallyDirty = 0
for (contact in getDirtyContacts())
try {
val lastHash = contact.getLastHashCode()
val currentHash = contact.dataHashCode()
if (lastHash == currentHash) {
// hash is code still the same, contact is not "really dirty" (only metadata been have changed)
Logger.log.log(Level.FINE, "Contact data hash has not changed, resetting dirty flag", contact)
contact.resetDirty()
} else {
Logger.log.log(Level.FINE, "Contact data has changed from hash $lastHash to $currentHash", contact)
reallyDirty++
}
} catch(e: FileNotFoundException) {
throw ContactsStorageException("Couldn't calculate hash code", e)
for (contact in findDirtyContacts()) {
val lastHash = contact.getLastHashCode()
val currentHash = contact.dataHashCode()
if (lastHash == currentHash) {
// hash is code still the same, contact is not "really dirty" (only metadata been have changed)
Logger.log.log(Level.FINE, "Contact data hash has not changed, resetting dirty flag", contact)
contact.resetDirty()
} else {
Logger.log.log(Level.FINE, "Contact data has changed from hash $lastHash to $currentHash", contact)
reallyDirty++
}
}
if (includeGroups)
reallyDirty += getDirtyGroups().size
reallyDirty += findDirtyGroups().size
return reallyDirty
}
/**
* Returns an array of local contacts/groups which have been changed locally (DIRTY != 0).
*/
@Throws(ContactsStorageException::class)
override fun getDirty(): List<LocalResource> {
val dirty = LinkedList<LocalResource>()
dirty.addAll(getDirtyContacts())
if (includeGroups)
dirty.addAll(getDirtyGroups())
return dirty
}
/**
* Returns an array of local contacts which don't have a file name yet.
*/
@Throws(ContactsStorageException::class)
override fun getWithoutFileName(): List<LocalResource> {
val nameless = LinkedList<LocalResource>()
nameless.addAll(queryContacts("${AndroidContact.COLUMN_FILENAME} IS NULL", null))
if (includeGroups)
nameless.addAll(queryGroups("${AndroidGroup.COLUMN_FILENAME} IS NULL", null))
return nameless
}
@Throws(ContactsStorageException::class)
fun getDeletedContacts() = queryContacts("${RawContacts.DELETED} != 0", null)
@Throws(ContactsStorageException::class)
fun getDirtyContacts() = queryContacts("${RawContacts.DIRTY} != 0", null)
@Throws(ContactsStorageException::class)
fun getDeletedGroups() = queryGroups("${Groups.DELETED} != 0", null)
@Throws(ContactsStorageException::class)
fun getDirtyGroups() = queryGroups("${Groups.DIRTY} != 0", null)
@Throws(ContactsStorageException::class)
fun getByGroupMembership(groupID: Long): List<LocalContact> {
try {
val ids = HashSet<Long>()
provider!!.query(syncAdapterURI(ContactsContract.Data.CONTENT_URI),
arrayOf(RawContacts.Data.RAW_CONTACT_ID),
"(${GroupMembership.MIMETYPE}=? AND ${GroupMembership.GROUP_ROW_ID}=?) OR (${CachedGroupMembership.MIMETYPE}=? AND ${CachedGroupMembership.GROUP_ID}=?)",
arrayOf(GroupMembership.CONTENT_ITEM_TYPE, groupID.toString(), CachedGroupMembership.CONTENT_ITEM_TYPE, groupID.toString()),
null)?.use { cursor ->
while (cursor.moveToNext())
ids += cursor.getLong(0)
}
return ids.map { id -> LocalContact(this, id, null, null) }
} catch (e: RemoteException) {
throw ContactsStorageException("Couldn't query contacts", e)
val ids = HashSet<Long>()
provider!!.query(syncAdapterURI(ContactsContract.Data.CONTENT_URI),
arrayOf(RawContacts.Data.RAW_CONTACT_ID),
"(${GroupMembership.MIMETYPE}=? AND ${GroupMembership.GROUP_ROW_ID}=?) OR (${CachedGroupMembership.MIMETYPE}=? AND ${CachedGroupMembership.GROUP_ID}=?)",
arrayOf(GroupMembership.CONTENT_ITEM_TYPE, groupID.toString(), CachedGroupMembership.CONTENT_ITEM_TYPE, groupID.toString()),
null)?.use { cursor ->
while (cursor.moveToNext())
ids += cursor.getLong(0)
}
return ids.map { findContactByID(it) }
}
@@ -272,29 +345,23 @@ class LocalAddressBook(
/**
* Finds the first group with the given title. If there is no group with this
* title, a new group is created.
* @param title title of the group to look for
* @return id of the group with given title
* @throws ContactsStorageException on contact provider errors
* @param title title of the group to look for
* @return id of the group with given title
* @throws RemoteException on content provider errors
*/
@Throws(ContactsStorageException::class)
fun findOrCreateGroup(title: String): Long {
try {
provider!!.query(syncAdapterURI(Groups.CONTENT_URI), arrayOf(Groups._ID),
"${Groups.TITLE}=?", arrayOf(title), null)?.use { cursor ->
if (cursor.moveToNext())
return cursor.getLong(0)
}
val values = ContentValues(1)
values.put(Groups.TITLE, title)
val uri = provider.insert(syncAdapterURI(Groups.CONTENT_URI), values)
return ContentUris.parseId(uri)
} catch(e: RemoteException) {
throw ContactsStorageException("Couldn't find local contact group", e)
provider!!.query(syncAdapterURI(Groups.CONTENT_URI), arrayOf(Groups._ID),
"${Groups.TITLE}=?", arrayOf(title), null)?.use { cursor ->
if (cursor.moveToNext())
return cursor.getLong(0)
}
val values = ContentValues(1)
values.put(Groups.TITLE, title)
val uri = provider.insert(syncAdapterURI(Groups.CONTENT_URI), values) ?: throw RemoteException("Couldn't create contact group")
return ContentUris.parseId(uri)
}
@Throws(ContactsStorageException::class)
fun removeEmptyGroups() {
// find groups without members
/** should be done using {@link Groups.SUMMARY_COUNT}, but it's not implemented in Android yet */
@@ -304,54 +371,4 @@ class LocalAddressBook(
}
}
@Throws(ContactsStorageException::class)
fun removeGroups() {
try {
provider!!.delete(syncAdapterURI(Groups.CONTENT_URI), null, null)
} catch(e: RemoteException) {
throw ContactsStorageException("Couldn't remove all groups", e)
}
}
// SETTINGS
@Throws(ContactsStorageException::class)
fun getMainAccount(): Account {
val accountManager = AccountManager.get(context)
val name = accountManager.getUserData(account, USER_DATA_MAIN_ACCOUNT_NAME)
val type = accountManager.getUserData(account, USER_DATA_MAIN_ACCOUNT_TYPE)
if (name != null && type != null)
return Account(name, type)
else
throw ContactsStorageException("Address book doesn't exist anymore")
}
fun setMainAccount(mainAccount: Account) {
val accountManager = AccountManager.get(context)
accountManager.setUserData(account, USER_DATA_MAIN_ACCOUNT_NAME, mainAccount.name)
accountManager.setUserData(account, USER_DATA_MAIN_ACCOUNT_TYPE, mainAccount.type)
}
@Throws(ContactsStorageException::class)
fun getURL(): String {
val accountManager = AccountManager.get(context)
return accountManager.getUserData(account, USER_DATA_URL) ?: throw ContactsStorageException("Address book has no URL")
}
fun setURL(url: String) {
val accountManager = AccountManager.get(context)
accountManager.setUserData(account, USER_DATA_URL, url)
}
override fun getCTag(): String? {
val accountManager = AccountManager.get(context)
return accountManager.getUserData(account, USER_DATA_CTAG)
}
override fun setCTag(cTag: String?) {
val accountManager = AccountManager.get(context)
accountManager.setUserData(account, USER_DATA_CTAG, cTag)
}
}
}
@@ -10,18 +10,20 @@ package at.bitfire.davdroid.resource
import android.accounts.Account
import android.content.ContentProviderClient
import android.content.ContentProviderOperation
import android.content.ContentUris
import android.content.ContentValues
import android.net.Uri
import android.os.RemoteException
import android.provider.CalendarContract
import android.provider.CalendarContract.*
import android.provider.CalendarContract.Calendars
import android.provider.CalendarContract.Events
import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.DavUtils
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.model.CollectionInfo
import at.bitfire.ical4android.*
import java.io.FileNotFoundException
import at.bitfire.davdroid.model.Collection
import at.bitfire.davdroid.model.SyncState
import at.bitfire.ical4android.AndroidCalendar
import at.bitfire.ical4android.AndroidCalendarFactory
import at.bitfire.ical4android.BatchOperation
import at.bitfire.ical4android.DateUtils
import java.util.*
import java.util.logging.Level
@@ -33,51 +35,41 @@ class LocalCalendar private constructor(
companion object {
val defaultColor = 0xFF8bc34a.toInt() // light green 500
private const val COLUMN_SYNC_STATE = Calendars.CAL_SYNC1
val COLUMN_CTAG = Calendars.CAL_SYNC1
val BASE_INFO_COLUMNS = arrayOf(
Events._ID,
Events._SYNC_ID,
LocalEvent.COLUMN_ETAG
)
@JvmStatic
@Throws(CalendarStorageException::class)
fun create(account: Account, provider: ContentProviderClient, info: CollectionInfo): Uri {
fun create(account: Account, provider: ContentProviderClient, info: Collection): Uri {
val values = valuesFromCollectionInfo(info, true)
// ACCOUNT_NAME and ACCOUNT_TYPE are required (see docs)! If it's missing, other apps will crash.
values.put(Calendars.ACCOUNT_NAME, account.name)
values.put(Calendars.ACCOUNT_TYPE, account.type)
// Email address for scheduling. Used by the calendar provider to determine whether the
// user is ORGANIZER/ATTENDEE for a certain event.
values.put(Calendars.OWNER_ACCOUNT, account.name)
// flag as visible & synchronizable at creation, might be changed by user at any time
values.put(Calendars.VISIBLE, 1)
values.put(Calendars.SYNC_EVENTS, 1)
return create(account, provider, values)
}
private fun valuesFromCollectionInfo(info: CollectionInfo, withColor: Boolean): ContentValues {
private fun valuesFromCollectionInfo(info: Collection, withColor: Boolean): ContentValues {
val values = ContentValues()
values.put(Calendars.NAME, info.url)
values.put(Calendars.NAME, info.url.toString())
values.put(Calendars.CALENDAR_DISPLAY_NAME, if (info.displayName.isNullOrBlank()) DavUtils.lastSegmentOfUrl(info.url) else info.displayName)
if (withColor)
values.put(Calendars.CALENDAR_COLOR, info.color ?: defaultColor)
values.put(Calendars.CALENDAR_COLOR, info.color ?: Constants.DAVDROID_GREEN_RGBA)
if (info.readOnly)
values.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_READ)
else {
if (info.privWriteContent && !info.forceReadOnly) {
values.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_OWNER)
values.put(Calendars.CAN_MODIFY_TIME_ZONE, 1)
values.put(Calendars.CAN_ORGANIZER_RESPOND, 1)
}
} else
values.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_READ)
info.timeZone?.let { tzData ->
info.timezone?.let { tzData ->
try {
val timeZone = DateUtils.parseVTimeZone(tzData)
timeZone.timeZoneId?.let { tzId ->
@@ -87,143 +79,174 @@ class LocalCalendar private constructor(
Logger.log.log(Level.WARNING, "Couldn't parse calendar default time zone", e)
}
}
values.put(Calendars.ALLOWED_REMINDERS, Reminders.METHOD_ALERT)
values.put(Calendars.ALLOWED_AVAILABILITY, "${Reminders.AVAILABILITY_TENTATIVE},${Reminders.AVAILABILITY_FREE},${Reminders.AVAILABILITY_BUSY}")
values.put(Calendars.ALLOWED_ATTENDEE_TYPES, "${CalendarContract.Attendees.TYPE_OPTIONAL},${CalendarContract.Attendees.TYPE_REQUIRED},${CalendarContract.Attendees.TYPE_RESOURCE}")
// add base values for Calendars
values.putAll(calendarBaseValues)
return values
}
}
override val tag: String
get() = "events-${account.name}-$id"
override fun eventBaseInfoColumns() = BASE_INFO_COLUMNS
override val title: String
get() = displayName ?: id.toString()
override var lastSyncState: SyncState?
get() = provider.query(calendarSyncURI(), arrayOf(COLUMN_SYNC_STATE), null, null, null)?.use { cursor ->
if (cursor.moveToNext())
return SyncState.fromString(cursor.getString(0))
else
null
}
set(state) {
val values = ContentValues(1)
values.put(COLUMN_SYNC_STATE, state.toString())
provider.update(calendarSyncURI(), values, null, null)
}
@Throws(CalendarStorageException::class)
fun update(info: CollectionInfo, updateColor: Boolean) =
fun update(info: Collection, updateColor: Boolean) =
update(valuesFromCollectionInfo(info, updateColor))
@Throws(CalendarStorageException::class)
override fun getAll(): List<LocalEvent> =
queryEvents("${Events.ORIGINAL_ID} IS NULL", null)
@Throws(CalendarStorageException::class)
override fun getDeleted() =
queryEvents("${Events.DELETED}!=0 AND ${Events.ORIGINAL_ID} IS NULL", null)
override fun findDeleted() =
queryEvents("${Events.DELETED} AND ${Events.ORIGINAL_ID} IS NULL", null)
@Throws(CalendarStorageException::class)
override fun getWithoutFileName() =
queryEvents("${Events._SYNC_ID} IS NULL AND ${Events.ORIGINAL_ID} IS NULL", null)
@Throws(FileNotFoundException::class, CalendarStorageException::class)
override fun getDirty(): List<LocalEvent> {
override fun findDirty(): List<LocalEvent> {
val dirty = LinkedList<LocalEvent>()
// get dirty events which are required to have an increased SEQUENCE value
for (localEvent in queryEvents("${Events.DIRTY}!=0 AND ${Events.ORIGINAL_ID} IS NULL", null)) {
val event = localEvent.event!!
val sequence = event.sequence
if (event.sequence == null) // sequence has not been assigned yet (i.e. this event was just locally created)
event.sequence = 0
else if (localEvent.weAreOrganizer)
event.sequence = sequence!! + 1
/*
* RFC 5545 3.8.7.4. Sequence Number
* When a calendar component is created, its sequence number is 0. It is monotonically incremented by the "Organizer's"
* CUA each time the "Organizer" makes a significant revision to the calendar component.
*/
for (localEvent in queryEvents("${Events.DIRTY} AND ${Events.ORIGINAL_ID} IS NULL", null)) {
try {
val event = requireNotNull(localEvent.event)
val nonGroupScheduled = event.attendees.isEmpty()
val weAreOrganizer = localEvent.weAreOrganizer
val sequence = event.sequence
if (sequence == null)
// sequence has not been assigned yet (i.e. this event was just locally created)
event.sequence = 0
else if (nonGroupScheduled || weAreOrganizer) // increase sequence
event.sequence = sequence + 1
} catch(e: Exception) {
Logger.log.log(Level.WARNING, "Couldn't check/increase sequence", e)
}
dirty += localEvent
}
return dirty
}
@Throws(CalendarStorageException::class)
override fun getCTag(): String? =
try {
provider.query(calendarSyncURI(), arrayOf(COLUMN_CTAG), null, null, null)?.use { cursor ->
if (cursor.moveToNext())
return cursor.getString(0)
}
null
} catch (e: RemoteException) {
throw CalendarStorageException("Couldn't read local (last known) CTag", e)
}
override fun findByName(name: String) =
queryEvents("${Events._SYNC_ID}=?", arrayOf(name)).firstOrNull()
@Throws(CalendarStorageException::class)
override fun setCTag(cTag: String?) {
try {
val values = ContentValues(1)
values.put(COLUMN_CTAG, cTag)
provider.update(calendarSyncURI(), values, null, null)
} catch (e: RemoteException) {
throw CalendarStorageException("Couldn't write local (last known) CTag", e)
}
override fun markNotDirty(flags: Int): Int {
val values = ContentValues(1)
values.put(LocalEvent.COLUMN_FLAGS, flags)
return provider.update(eventsSyncURI(), values,
"${Events.CALENDAR_ID}=? AND NOT ${Events.DIRTY} AND ${Events.ORIGINAL_ID} IS NULL",
arrayOf(id.toString()))
}
@Throws(CalendarStorageException::class)
override fun removeNotDirtyMarked(flags: Int): Int {
var deleted = 0
// list all non-dirty events with the given flags and delete every row + its exceptions
provider.query(eventsSyncURI(), arrayOf(Events._ID),
"${Events.CALENDAR_ID}=? AND NOT ${Events.DIRTY} AND ${Events.ORIGINAL_ID} IS NULL AND ${LocalEvent.COLUMN_FLAGS}=?",
arrayOf(id.toString(), flags.toString()), null)?.use { cursor ->
val batch = BatchOperation(provider)
while (cursor.moveToNext()) {
val id = cursor.getLong(0)
// delete event and possible exceptions (content provider doesn't delete exceptions itself)
batch.enqueue(BatchOperation.CpoBuilder
.newDelete(eventsSyncURI())
.withSelection("${Events._ID}=? OR ${Events.ORIGINAL_ID}=?", arrayOf(id.toString(), id.toString())))
}
deleted = batch.commit()
}
return deleted
}
override fun forgetETags() {
val values = ContentValues(1)
values.putNull(LocalEvent.COLUMN_ETAG)
provider.update(eventsSyncURI(), values, "${Events.CALENDAR_ID}=?",
arrayOf(id.toString()))
}
fun processDirtyExceptions() {
try {
// process deleted exceptions
Logger.log.info("Processing deleted exceptions")
provider.query(
syncAdapterURI(Events.CONTENT_URI),
arrayOf(Events._ID, Events.ORIGINAL_ID, LocalEvent.COLUMN_SEQUENCE),
"${Events.DELETED}!=0 AND ${Events.ORIGINAL_ID} IS NOT NULL", null, null)?.use { cursor ->
while (cursor.moveToNext()) {
Logger.log.fine("Found deleted exception, removing; then re-scheduling original event")
val id = cursor.getLong(0) // can't be null (by definition)
val originalID = cursor.getLong(1) // can't be null (by query)
// process deleted exceptions
Logger.log.info("Processing deleted exceptions")
provider.query(
syncAdapterURI(Events.CONTENT_URI),
arrayOf(Events._ID, Events.ORIGINAL_ID, LocalEvent.COLUMN_SEQUENCE),
"${Events.CALENDAR_ID}=? AND ${Events.DELETED} AND ${Events.ORIGINAL_ID} IS NOT NULL",
arrayOf(id.toString()), null)?.use { cursor ->
while (cursor.moveToNext()) {
Logger.log.fine("Found deleted exception, removing and re-scheduling original event (if available)")
val id = cursor.getLong(0) // can't be null (by definition)
val originalID = cursor.getLong(1) // can't be null (by query)
val batch = BatchOperation(provider)
val batch = BatchOperation(provider)
// get original event's SEQUENCE
provider.query(
syncAdapterURI(ContentUris.withAppendedId(Events.CONTENT_URI, originalID)),
arrayOf(LocalEvent.COLUMN_SEQUENCE),
null, null, null)?.use { cursor2 ->
// get original event's SEQUENCE
provider.query(
syncAdapterURI(ContentUris.withAppendedId(Events.CONTENT_URI, originalID)),
arrayOf(LocalEvent.COLUMN_SEQUENCE),
null, null, null)?.use { cursor2 ->
if (cursor2.moveToNext()) {
// original event is available
val originalSequence = if (cursor2.isNull(0)) 0 else cursor2.getInt(0)
// re-schedule original event and set it to DIRTY
batch.enqueue(BatchOperation.Operation(
ContentProviderOperation.newUpdate(syncAdapterURI(ContentUris.withAppendedId(Events.CONTENT_URI, originalID)))
.withValue(LocalEvent.COLUMN_SEQUENCE, originalSequence + 1)
.withValue(Events.DIRTY, 1)
))
batch.enqueue(BatchOperation.CpoBuilder
.newUpdate(syncAdapterURI(ContentUris.withAppendedId(Events.CONTENT_URI, originalID)))
.withValue(LocalEvent.COLUMN_SEQUENCE, originalSequence + 1)
.withValue(Events.DIRTY, 1))
}
// remove exception
batch.enqueue(BatchOperation.Operation(
ContentProviderOperation.newDelete(syncAdapterURI(ContentUris.withAppendedId(Events.CONTENT_URI, id)))
))
batch.commit()
}
}
// process dirty exceptions
Logger.log.info("Processing dirty exceptions")
provider.query(
syncAdapterURI(Events.CONTENT_URI),
arrayOf(Events._ID, Events.ORIGINAL_ID, LocalEvent.COLUMN_SEQUENCE),
"${Events.DIRTY}!=0 AND ${Events.ORIGINAL_ID} IS NOT NULL", null, null)?.use { cursor ->
while (cursor.moveToNext()) {
Logger.log.fine("Found dirty exception, increasing SEQUENCE to re-schedule")
val id = cursor.getLong(0) // can't be null (by definition)
val originalID = cursor.getLong(1) // can't be null (by query)
val sequence = if (cursor.isNull(2)) 0 else cursor.getInt(2)
val batch = BatchOperation(provider)
// original event to DIRTY
batch.enqueue(BatchOperation.Operation (
ContentProviderOperation.newUpdate(syncAdapterURI(ContentUris.withAppendedId(Events.CONTENT_URI, originalID)))
.withValue(Events.DIRTY, 1)
))
// increase SEQUENCE and set DIRTY to 0
batch.enqueue(BatchOperation.Operation (
ContentProviderOperation.newUpdate(syncAdapterURI(ContentUris.withAppendedId(Events.CONTENT_URI, id)))
.withValue(LocalEvent.COLUMN_SEQUENCE, sequence + 1)
.withValue(Events.DIRTY, 0)
))
batch.commit()
}
// completely remove deleted exception
batch.enqueue(BatchOperation.CpoBuilder.newDelete(syncAdapterURI(ContentUris.withAppendedId(Events.CONTENT_URI, id))))
batch.commit()
}
}
// process dirty exceptions
Logger.log.info("Processing dirty exceptions")
provider.query(
syncAdapterURI(Events.CONTENT_URI),
arrayOf(Events._ID, Events.ORIGINAL_ID, LocalEvent.COLUMN_SEQUENCE),
"${Events.CALENDAR_ID}=? AND ${Events.DIRTY} AND ${Events.ORIGINAL_ID} IS NOT NULL",
arrayOf(id.toString()), null)?.use { cursor ->
while (cursor.moveToNext()) {
Logger.log.fine("Found dirty exception, increasing SEQUENCE to re-schedule")
val id = cursor.getLong(0) // can't be null (by definition)
val originalID = cursor.getLong(1) // can't be null (by query)
val sequence = if (cursor.isNull(2)) 0 else cursor.getInt(2)
val batch = BatchOperation(provider)
// original event to DIRTY
batch.enqueue(BatchOperation.CpoBuilder
.newUpdate(syncAdapterURI(ContentUris.withAppendedId(Events.CONTENT_URI, originalID)))
.withValue(Events.DIRTY, 1))
// increase SEQUENCE and set DIRTY to 0
batch.enqueue(BatchOperation.CpoBuilder
.newUpdate(syncAdapterURI(ContentUris.withAppendedId(Events.CONTENT_URI, id)))
.withValue(LocalEvent.COLUMN_SEQUENCE, sequence + 1)
.withValue(Events.DIRTY, 0))
batch.commit()
}
} catch (e: RemoteException) {
throw CalendarStorageException("Couldn't process locally modified exception", e)
}
}
@@ -235,4 +258,4 @@ class LocalCalendar private constructor(
}
}
}
Loaded 100 of 427 files, more files were not shown because too many files have changed in this diff. Show more