Compare commits

...

22 Commits
v1.7 ... v1.7.3

Author SHA1 Message Date
Ricki Hirner
f1f16ffe4f Version bump to 1.7.3 2017-09-10 20:18:18 +02:00
Ricki Hirner
0214a3adb5 Fetch translations from Transifex 2017-09-10 20:17:31 +02:00
Ricki Hirner
7cea2a8379 Make SyncManager Closeable 2017-09-10 19:50:12 +02:00
Ricki Hirner
d07df21b47 Don't allow calendars which don't support VEVENT and/or VTODO to be selected for sync 2017-09-10 14:00:37 +02:00
Ricki Hirner
0459c9844c Upgrade to okhttp/3.9.0 2017-09-10 01:30:53 +02:00
Ricki Hirner
de2b4ad506 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:33:24 +02:00
Ricki Hirner
38e28b1b20 Update cert4android 2017-09-07 14:25:23 +02:00
Ricki Hirner
da2567c49c Fetch cert4android translations from Transifex 2017-09-01 15:13:07 +02:00
Ricki Hirner
a51e903324 Fetch translations from Transifex 2017-09-01 15:12:35 +02:00
Ricki Hirner
5da5506754 Version bump to 1.7.2 2017-09-01 15:11:26 +02:00
Ricki Hirner
e0cb048802 donation popup; ProGuard
* don't show donation popup too often
* ProGuard fixes
2017-08-31 01:03:54 +02:00
Ricki Hirner
b1a09eecb5 Version bump to 1.7.2-beta1 2017-08-30 21:09:47 +02:00
Ricki Hirner
1bb6a0de89 Fetch cert4android translations from Transifex 2017-08-30 21:09:47 +02:00
Ricki Hirner
678b3824c2 Fetch translations from Transifex 2017-08-30 21:09:42 +02:00
Ricki Hirner
83054635d5 Make event color support optional (opt-in) 2017-08-30 20:21:36 +02:00
Ricki Hirner
33a6494389 Update to Android support library 26.0.1 2017-08-30 12:28:08 +02:00
Ricki Hirner
5913892bb6 Optimize logging 2017-08-29 18:05:59 +02:00
Ricki Hirner
69b3273fdc Fetch translations from Transifex 2017-08-22 15:17:20 +02:00
Ricki Hirner
88c8f88c88 Fetch cert4android translations from Transifex 2017-08-22 15:15:36 +02:00
Ricki Hirner
28af03749a Version bump to 1.7.1 2017-08-22 15:14:36 +02:00
Ricki Hirner
c54da0812a Add donation startup fragment 2017-08-22 14:56:38 +02:00
Ricki Hirner
7454d21a52 ProGuard: keep ThreeTen; update build tools and Kotlin version 2017-08-22 14:18:31 +02:00
47 changed files with 886 additions and 395 deletions

View File

@@ -12,13 +12,13 @@ apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 26
buildToolsVersion '26.0.0'
buildToolsVersion '26.0.1'
defaultConfig {
applicationId "at.bitfire.davdroid"
resValue "string", "packageID", applicationId
versionCode 163
versionCode 167
buildConfigField "long", "buildTime", System.currentTimeMillis() + "L"
minSdkVersion 16
@@ -30,19 +30,19 @@ android {
productFlavors {
standard {
versionName "1.7"
versionName "1.7.3"
buildConfigField "boolean", "customCerts", "true"
}
gplay {
versionName "1.7-gplay"
versionName "1.7.3-gplay"
buildConfigField "boolean", "customCerts", "true"
}
icloud {
applicationId "at.bitfire.cloudsync"
resValue "string", "packageID", applicationId
versionName "1.7-cloud"
versionName "1.7.3-cloud"
buildConfigField "at.bitfire.vcard4android.GroupMethod", "settingContactGroupMethod", "at.bitfire.vcard4android.GroupMethod.GROUP_VCARDS"
}
soldupe {
@@ -50,7 +50,7 @@ android {
resValue "string", "packageID", applicationId
minSdkVersion 21
versionName "1.7-soldupe"
versionName "1.7.3-soldupe"
buildConfigField "boolean", "customCerts", "true"
buildConfigField "at.bitfire.vcard4android.GroupMethod", "settingContactGroupMethod", "at.bitfire.vcard4android.GroupMethod.CATEGORIES"
}
@@ -122,18 +122,14 @@ dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
//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.+'
compile 'com.android.support:appcompat-v7:26.0.1'
compile 'com.android.support:cardview-v7:26.0.1'
compile 'com.android.support:design:26.0.1'
compile 'com.android.support:preference-v14:26.0.1'
compile 'com.github.yukuku:ambilwarna:2.0.1'
compile 'com.squareup.okhttp3:logging-interceptor:3.8.1'
compile 'com.squareup.okhttp3:logging-interceptor:3.9.0'
compile 'commons-io:commons-io:2.5'
compile 'dnsjava:dnsjava:2.1.8'
compile 'org.apache.commons:commons-lang3:3.6'
@@ -149,8 +145,8 @@ dependencies {
exclude group: 'com.android.support', module: 'support-annotations'
}
androidTestCompile 'junit:junit:4.12'
androidTestCompile 'com.squareup.okhttp3:mockwebserver:3.8.1'
androidTestCompile 'com.squareup.okhttp3:mockwebserver:3.9.0'
testCompile 'junit:junit:4.12'
testCompile 'com.squareup.okhttp3:mockwebserver:3.8.1'
testCompile 'com.squareup.okhttp3:mockwebserver:3.9.0'
}

View File

@@ -12,20 +12,23 @@
-allowaccessmodification
-dontpreverify
# Kotlin
-dontwarn kotlin.**
# ez-vcard
-dontwarn ezvcard.io.json.** # JSON serializer (for jCards) not used
-dontwarn freemarker.** # freemarker templating library (for creating hCards) not used
-dontwarn org.jsoup.** # jsoup library (for hCard parsing) not used
-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 net.fortuna.ical4j.model.**
-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)
-keep class org.threeten.bp.** { *; } # keep ThreeTen (for time zone processing)
# okhttp
-dontwarn okio.**

View File

@@ -16,10 +16,13 @@ import java.io.IOException;
import java.net.Socket;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import at.bitfire.cert4android.CustomCertManager;
import okhttp3.mockwebserver.MockWebServer;
import static android.support.test.InstrumentationRegistry.getInstrumentation;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.TestCase.assertFalse;
import static org.apache.commons.lang3.ArrayUtils.contains;
@@ -27,18 +30,21 @@ import static org.junit.Assert.assertTrue;
public class SSLSocketFactoryCompatTest {
CustomCertManager certMgr;
SSLSocketFactoryCompat factory;
MockWebServer server = new MockWebServer();
@Before
public void startServer() throws Exception {
factory = new SSLSocketFactoryCompat(new CustomCertManager(getTargetContext().getApplicationContext(), true));
certMgr = new CustomCertManager(getInstrumentation().getContext(), true, null);
factory = new SSLSocketFactoryCompat(certMgr);
server.start();
}
@After
public void stopServer() throws Exception {
server.shutdown();
certMgr.close();
}

View File

@@ -10,6 +10,8 @@ package at.bitfire.davdroid.model;
import android.content.ContentValues;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
@@ -30,8 +32,20 @@ import static org.junit.Assert.assertTrue;
public class CollectionInfoTest {
HttpClient httpClient;
MockWebServer server = new MockWebServer();
@Before
public void setUp() {
httpClient = new HttpClient.Builder().build();
}
@After
public void shutDown() {
httpClient.close();
}
@Test
public void testFromDavResource() throws IOException, HttpException, DavException {
// r/w address book
@@ -48,7 +62,7 @@ public class CollectionInfoTest {
"</response>" +
"</multistatus>"));
DavResource dav = new DavResource(HttpClient.create(null), server.url("/"));
DavResource dav = new DavResource(httpClient.getOkHttpClient(), server.url("/"));
dav.propfind(0, ResourceType.NAME);
CollectionInfo info = new CollectionInfo(dav);
assertEquals(CollectionInfo.Type.ADDRESS_BOOK, info.getType());
@@ -72,7 +86,7 @@ public class CollectionInfoTest {
"</response>" +
"</multistatus>"));
dav = new DavResource(HttpClient.create(null), server.url("/"));
dav = new DavResource(httpClient.getOkHttpClient(), server.url("/"));
dav.propfind(0, ResourceType.NAME);
info = new CollectionInfo(dav);
assertEquals(CollectionInfo.Type.CALENDAR, info.getType());

View File

@@ -23,7 +23,6 @@ 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;
@@ -40,7 +39,7 @@ public class DavResourceFinderTest {
MockWebServer server = new MockWebServer();
DavResourceFinder finder;
OkHttpClient client;
HttpClient client;
LoginCredentials credentials;
private static final String
@@ -62,8 +61,9 @@ public class DavResourceFinderTest {
credentials = new LoginCredentials(URI.create("/"), "mock", "12345");
finder = new DavResourceFinder(getTargetContext(), credentials);
client = HttpClient.create(null);
client = HttpClient.addAuthentication(client, credentials.getUserName(), credentials.getPassword());
client = new HttpClient.Builder()
.addAuthentication(null, credentials.getUserName(), credentials.getPassword())
.build();
}
@After
@@ -76,7 +76,7 @@ public class DavResourceFinderTest {
ServiceInfo info;
// before dav.propfind(), no info is available
DavResource dav = new DavResource(client, server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL));
DavResource dav = new DavResource(client.getOkHttpClient(), server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL));
finder.rememberIfAddressBookOrHomeset(dav, info = new ServiceInfo());
assertEquals(0, info.getCollections().size());
assertEquals(0, info.getHomeSets().size());
@@ -89,7 +89,7 @@ public class DavResourceFinderTest {
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 = new DavResource(client.getOkHttpClient(), server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK));
dav.propfind(0, ResourceType.NAME);
finder.rememberIfAddressBookOrHomeset(dav, info = new ServiceInfo());
assertEquals(1, info.getCollections().size());

View File

@@ -13,6 +13,7 @@
<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">Aperçu de la version finale</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>
@@ -21,7 +22,7 @@
<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">L\'application OpenTasks n\'est pas installée</string>
<string name="startup_opentasks_not_installed_message">L\'application OpenTasks n\'est pas disponible, donc DAVdroid ne pourra pas synchroniser des listes de tâches.</string>
<string name="startup_opentasks_reinstall_davdroid">Après l\'installation OpenTasks, vous devez RE-INSTALLER DAVdroid et ajoutez vos comptes à nouveau (bug Android).</string>
<string name="startup_opentasks_not_installed_install">Installer OpenTasks</string>
@@ -40,12 +41,13 @@
<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_news_updates">Actualités &amp; mises à jour</string>
<string name="navigation_drawer_external_links">Liens externes</string>
<string name="navigation_drawer_website">Site Web</string>
<string name="navigation_drawer_faq">FAQ</string>
<string name="navigation_drawer_faq">Foire aux questions</string>
<string name="navigation_drawer_forums">Aide/Forum</string>
<string name="navigation_drawer_donate">Faire un don</string>
<string name="account_list_empty">Bienvenue sur DAVdroid!\n\nVous pouvez maintenant ajouter un compte CalDAV/CardDAV.</string>
<string name="account_list_empty">Bienvenue sur DAVdroid!\n\nVous pouvez maintenant ajouter un compte CalDAV ou CardDAV.</string>
<string name="accounts_global_sync_disabled">La synchronisation automatique globale est désactivée</string>
<string name="accounts_global_sync_enable">Activer</string>
<!--DavService-->
@@ -95,7 +97,7 @@
<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">Autorisations d\'accès aux contacts</string>
<string name="permissions_contacts_details">Pour synchroniser les carnets d\'adresses de CardDAV avec votre carnet d\'adresses local, DAVdroid a besoin d\'accéder à vos contacts.</string>
<string name="permissions_contacts_request">Demande d\'autorisations d\'accéder aux contacts</string>
<string name="permissions_opentasks">Autorisations OpenTasks</string>
@@ -124,8 +126,8 @@
<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_querying_server">Veuillez patienter, nous interrogeons le serveur ...</string>
<string name="login_no_caldav_carddav">Aucun accès possible au service CalDAV ou CardDAV.</string>
<string name="login_view_logs">Voir infos de débogage</string>
<!--AccountSettingsActivity-->
<string name="settings_title">Paramètres: %s</string>
@@ -164,6 +166,10 @@
<string name="settings_sync_wifi_only">Synchronisation en Wifi seulement</string>
<string name="settings_sync_wifi_only_on">La synchronisation est limitée aux connexions WiFi</string>
<string name="settings_sync_wifi_only_off">Le type de connexion n\'est pas pris en charge</string>
<string name="settings_sync_wifi_only_ssids">Restrictions concernant le nom de réseau WiFi (SSID)</string>
<string name="settings_sync_wifi_only_ssids_on">Synchronisation possible seulement en %s</string>
<string name="settings_sync_wifi_only_ssids_off">Toutes les connexions WiFi seront utilisées</string>
<string name="settings_sync_wifi_only_ssids_message">Liste des points d\'accès WiFi (SSID) autorisés, séparés par des virgules. (Laissez vide pour tous)</string>
<string name="settings_carddav">CardDAV</string>
<string name="settings_contact_group_method">Méthode pour les contacts de type groupe</string>
<string-array name="settings_contact_group_method_values">
@@ -185,6 +191,10 @@
<string name="settings_manage_calendar_colors">Choisir couleur du calendrier</string>
<string name="settings_manage_calendar_colors_on">Les couleurs de calendrier sont gérées par DAVdroid</string>
<string name="settings_manage_calendar_colors_off">Les couleurs de calendrier ne sont pas gérées par DAVdroid</string>
<string name="settings_event_colors">Couleur associée aux événements</string>
<string name="settings_event_colors_on">Synchroniser la couleur associée aux événements</string>
<string name="settings_event_colors_off">Ne pas synchroniser la couleur associée aux événements</string>
<string name="settings_event_colors_off_confirm">Modifier la couleur associée aux événements peut affecter les valeurs déjà synchronisées.</string>
<!--collection management-->
<string name="create_addressbook">Créer un carnet d\'adresses</string>
<string name="create_addressbook_display_name_hint">Mon carnet d\'adresses</string>

View File

@@ -17,7 +17,6 @@
<string name="startup_development_version">プレビュー リリース</string>
<string name="startup_development_version_message">これは %s の開発版です。期待した通りに動作しない可能性があります。フィードバックを歓迎します。</string>
<string name="startup_development_version_give_feedback">フィードバックする</string>
<string name="startup_development_version_feedback_url">https://davdroid.bitfire.at/forums/?pk_campaign=davdroid-app</string>
<string name="startup_donate">オープンソース情報</string>
<string name="startup_donate_message">あなたがオープンソース ソフトウェア (GPLv3) の DAVdroid を使用していただくことに、私たちは満足しています。 DAVdroid の開発はハードワークで、何千もの作業時間がかかりました。寄付をご検討ください。</string>
<string name="startup_donate_now">寄付ページを表示</string>
@@ -184,6 +183,10 @@
<string name="settings_manage_calendar_colors">カレンダーの色を管理</string>
<string name="settings_manage_calendar_colors_on">カレンダーの色は DAVdroid が管理します</string>
<string name="settings_manage_calendar_colors_off">カレンダーの色を DAVdroid が設定しません</string>
<string name="settings_event_colors">イベントカラーサポート</string>
<string name="settings_event_colors_on">イベントカラーを同期</string>
<string name="settings_event_colors_off">イベントカラーを同期しない</string>
<string name="settings_event_colors_off_confirm">イベントカラーをオフにすると、すでに同期しているイベントカラーが削除されることがあります。</string>
<!--collection management-->
<string name="create_addressbook">アドレス帳を作成</string>
<string name="create_addressbook_display_name_hint">マイ アドレス帳</string>

View File

@@ -0,0 +1,154 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources xmlns:tools="http://schemas.android.com/tools">
<!--common strings-->
<string name="app_name">DAVdroid</string>
<string name="account_title_address_book">DAVdroid-adressebok</string>
<string name="address_books_authority_title">Adressebøker</string>
<string name="help">Hjelp</string>
<string name="manage_accounts">Behandle kontoer</string>
<string name="please_wait">Vent…</string>
<string name="send">Send</string>
<!--startup dialogs-->
<string name="startup_battery_optimization">Batterioptimisering</string>
<string name="startup_battery_optimization_message">Det kan hende Android skrur av/reduserer DAVdroid-synkronisering etter et par dager. For å forhindre dette, skru av batterioptimisering.</string>
<string name="startup_battery_optimization_disable">Skru av for DAVdroid</string>
<string name="startup_dont_show_again">Ikke vis igjen</string>
<string name="startup_development_version">Forhåndsutgivelse</string>
<string name="startup_development_version_message">Dette er en utviklingsversjon av %s. Det kan hende ting ikke svarer til forventningene. Dine tilbakemeldinger er kjærkomne.</string>
<string name="startup_development_version_give_feedback">Gi tilbakemelding</string>
<string name="startup_donate">Friprog-informasjon</string>
<string name="startup_donate_message">Vi er glade for at du bruker DAVdroid, som er fri programvare (GPLv3). Siden utvikling av DAVdroid er hardt arbeid og har tatt tusenvis av arbeidstimer, bes du overveie en donasjon.</string>
<string name="startup_donate_now">Vis donasjonsside</string>
<string name="startup_donate_later">Kanskje senere</string>
<string name="startup_google_play_accounts_removed_more_info">Mer informasjon</string>
<!--AboutActivity-->
<string name="about_license_terms">Lisensvilkår</string>
<!--global settings-->
<string name="logging_to_external_storage">Logger til ekstern lagringsmedium: %s</string>
<string name="logging_to_external_storage_warning">Slett logger så snart som mulig!</string>
<string name="logging_couldnt_create_file">Kan ikke opprette ekstern loggfil: %s</string>
<string name="logging_no_external_storage">Fant ingen ekstern lagringsplass</string>
<!--AccountsActivity-->
<string name="navigation_drawer_open">Åpne navigasjonsskuff</string>
<string name="navigation_drawer_close">Lukk navigasjonsskuff</string>
<string name="navigation_drawer_about">Om / Lisens</string>
<string name="navigation_drawer_settings">Innstillinger</string>
<string name="navigation_drawer_external_links">Eksterne lenker</string>
<string name="navigation_drawer_website">Nettside</string>
<string name="navigation_drawer_faq">O-S-S</string>
<string name="navigation_drawer_forums">Hjelp / Forum</string>
<string name="navigation_drawer_donate">Doner</string>
<string name="accounts_global_sync_disabled">Systemomspennende automatisk synkronisering avskrudd</string>
<string name="accounts_global_sync_enable">Skru på</string>
<!--DavService-->
<string name="dav_service_refresh_failed">Tjenesteoppdagelse mislyktes</string>
<string name="dav_service_refresh_couldnt_refresh">Kunne ikke gjenoppfriske innsamlingsliste</string>
<!--AppSettingsActivity-->
<string name="app_settings">Innstillinger</string>
<string name="app_settings_user_interface">Brukergrensesnitt</string>
<string name="app_settings_reset_hints">Tilbakestill hint</string>
<string name="app_settings_reset_hints_summary">Skrur på hint som har blitt avslått tidligere</string>
<string name="app_settings_reset_hints_success">Alle hint vil bli vist igjen</string>
<string name="app_settings_connection">Tilkobling</string>
<string name="app_settings_override_proxy">Overstyr mellomtjenerinnstillinger</string>
<string name="app_settings_override_proxy_on">Bruk egendefinerte mellomtjenerinnstillinger</string>
<string name="app_settings_override_proxy_off">Bruk systemets forvalgte mellomtjenerinnstillinger</string>
<string name="app_settings_override_proxy_host">Vertsnavn for HTTP-mellomtjener</string>
<string name="app_settings_override_proxy_port">HTTP-mellomtjeningsport</string>
<string name="app_settings_security">Sikkerhet</string>
<string name="app_settings_distrust_system_certs">Fjern tiltro til systemsertifikater</string>
<string name="app_settings_debug">Feilretting</string>
<string name="app_settings_log_to_external_storage">Logg til ekstern fil</string>
<string name="app_settings_log_to_external_storage_on">Logger til eksternt lagringsmedium (hvis tilgjengelig)</string>
<string name="app_settings_show_debug_info">Vis feilrettingsinfo</string>
<string name="app_settings_show_debug_info_details">Vis/del programvare- og oppsettsdetaljer</string>
<!--AccountActivity-->
<string name="account_synchronize_now">Synkroniser nå</string>
<string name="account_synchronizing_now">Synkroniserer nå</string>
<string name="account_settings">Kontoinnstillinger</string>
<string name="account_rename">Gi konto nytt navn</string>
<string name="account_rename_rename">Gi nytt navn</string>
<string name="account_delete">Slett konto</string>
<string name="account_delete_confirmation_title">Vil du virkeling slette kontoen?</string>
<string name="account_refresh_address_book_list">Gjenoppfrisk adressebokliste</string>
<string name="account_create_new_address_book">Opprett ny adressebok</string>
<string name="account_refresh_calendar_list">Gjenoppfrisk kalenderliste</string>
<string name="account_create_new_calendar">Opprett ny kalender</string>
<!--PermissionsActivity-->
<string name="permissions_title">DAVdroid-tilgang</string>
<string name="permissions_calendar">Kalender-tilgang</string>
<string name="permissions_calendar_request">Forespør kalender-tilganger</string>
<string name="permissions_contacts">Kontakt-tilgang</string>
<string name="permissions_contacts_request">Forespør kontakt-tilgang</string>
<!--AddAccountActivity-->
<string name="login_title">Legg til konto</string>
<string name="login_email_address">E-postadresse</string>
<string name="login_email_address_error">Gyldig e-postadresse påkrevd</string>
<string name="login_password">Passord</string>
<string name="login_password_required">Passord kreves</string>
<string name="login_url_must_be_http_or_https">Nettadresse må begynned med http(s)://</string>
<string name="login_url_host_name_required">Vertsnavn kreves</string>
<string name="login_back">Tilbake</string>
<string name="login_create_account">Opprett konto</string>
<string name="login_account_name">Kontonavn</string>
<string name="login_account_name_required">Kontonavn påkrevd</string>
<string name="login_account_not_created">Kontonavnet kan ikke opprettes</string>
<string name="login_configuration_detection">Oppdagelse av oppsett</string>
<string name="login_querying_server">Vent, spør tjener…</string>
<string name="login_no_caldav_carddav">Fant ikke CalDAV eller CardDAV-tjeneste.</string>
<string name="login_view_logs">Vis logger</string>
<!--AccountSettingsActivity-->
<string name="settings_title">Innstillinger: %s</string>
<string name="settings_authentication">Identitetsbekreftelse</string>
<string name="settings_username">Brukernavn</string>
<string name="settings_enter_username">Skriv inn brukernavn:</string>
<string name="settings_password">Passord</string>
<string name="settings_password_summary">Oppdater passordet i henhold til din tjener.</string>
<string name="settings_enter_password">Skriv inn passordet ditt:</string>
<string name="settings_sync">Synkronisering</string>
<string name="settings_sync_interval_contacts">Intervall for kontaktsynkronisering</string>
<string name="settings_sync_summary_manually">Åpne manuelt</string>
<string name="settings_sync_wifi_only">Bare synk. over Wi-Fi</string>
<string name="settings_sync_wifi_only_off">Tilkoblingstypen blir ikke tatt i betraktning</string>
<string name="settings_carddav">CardDAV</string>
<string name="settings_caldav">CalDAV</string>
<string name="settings_sync_time_range_past_none">Alle gjøremål vil bli synkronisert</string>
<plurals name="settings_sync_time_range_past_days">
<item quantity="one">Gjøremål for mer enn én dag siden vil bli sett bort fra</item>
<item quantity="other">Gjøremål for mer enn %d dager siden vil bli sett bort fra</item>
</plurals>
<string name="settings_manage_calendar_colors">Velg kalenderfarger</string>
<string name="settings_manage_calendar_colors_on">Kalenderfarger behandles av DAVdroid</string>
<string name="settings_manage_calendar_colors_off">Kalenderfarger settes ikke av DAVdroid</string>
<!--collection management-->
<string name="create_addressbook">Opprett adressebok</string>
<string name="create_addressbook_display_name_hint">Min adressebok</string>
<string name="create_calendar">Opprett CalDAV-samling</string>
<string name="create_calendar_display_name_hint">Min kalender</string>
<string name="create_calendar_time_zone">Tidssone</string>
<string name="create_calendar_type">Samlingstype:</string>
<string name="create_calendar_type_only_events">Kalender (bare hendelser)</string>
<string name="create_calendar_type_only_tasks">Gjøremålsliste (bare gjøremål)</string>
<string name="create_calendar_type_events_and_tasks">Kombinert (hendelser og gjøremål)</string>
<string name="create_collection_color">Velg en samlingsfarge</string>
<string name="create_collection_creating">Oppretter samling</string>
<string name="create_collection_display_name">Vis navn (tittel) for denne samlingen:</string>
<string name="create_collection_display_name_required">Tittel kreves</string>
<string name="create_collection_description">Beskrivelse (valgfri):</string>
<string name="create_collection_create">Opprett</string>
<string name="delete_collection">Slett samling</string>
<string name="delete_collection_confirm_title">Er du sikker?</string>
<string name="delete_collection_deleting_collection">Sletter samling</string>
<!--ExceptionInfoFragment-->
<string name="exception">En feil har inntruffet</string>
<string name="exception_httpexception">En HTTP-feil har inntruffet.</string>
<string name="exception_ioexception">En I/O-feil har inntruffet.</string>
<string name="exception_show_details">Vis detaljer</string>
<!--sync errors and DebugInfoActivity-->
<string name="debug_info_title">Feilrettingsinfo</string>
<string name="sync_error_tasks">Gjøremålssynkronisering mislyktes (%s)</string>
<string name="sync_error_unauthorized">Brukernavn-/passord feil</string>
<!--cert4android-->
<string name="certificate_notification_connection_security">DAVdroid: Tilkoblingssikkerhet</string>
<string name="trust_certificate_unknown_certificate_found">DAVdroid har støtt på et ukjent sertifikat. Har du tiltro til det?</string>
</resources>

View File

@@ -17,7 +17,6 @@
<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>

View File

@@ -46,6 +46,7 @@
<string name="navigation_drawer_external_links">Zewnętrzne odnośniki</string>
<string name="navigation_drawer_website">Strona WWW</string>
<string name="navigation_drawer_faq">FQA</string>
<string name="navigation_drawer_forums">Pomoc / Forum</string>
<string name="navigation_drawer_donate">Dotacja</string>
<string name="account_list_empty">Witamy w DAVdroid!\n\nMożesz teraz dodać konto CalDAV/CardDAV.</string>
<string name="accounts_global_sync_disabled">Automatyczna synchronizacja dla całego systemu jest wyłączona</string>
@@ -165,6 +166,10 @@
<string name="settings_sync_wifi_only">Synchronizuj tylko przez WiFi</string>
<string name="settings_sync_wifi_only_on">Synchronizacja jest ograniczony do połączeń WiFi</string>
<string name="settings_sync_wifi_only_off">Rodzaj połączenia nie jest brany pod uwagę</string>
<string name="settings_sync_wifi_only_ssids">Ograniczenia WiFi SSID</string>
<string name="settings_sync_wifi_only_ssids_on">Będzie synchronizować tylko w %s</string>
<string name="settings_sync_wifi_only_ssids_off">Wszystkie połączenia WiFi będą używane</string>
<string name="settings_sync_wifi_only_ssids_message">Nazwy oddzielone przecinkami (SSID) dozwolonych sieci WiFi (pozostaw puste dla wszystkich)</string>
<string name="settings_carddav">CardDAV</string>
<string name="settings_contact_group_method">Metoda grupowania kontaktów</string>
<string-array name="settings_contact_group_method_values">
@@ -184,6 +189,10 @@
<string name="settings_manage_calendar_colors">Zarządzaj kolorami kalendarza</string>
<string name="settings_manage_calendar_colors_on">Kolory kalendarza są zarządzane przez DAVdroid</string>
<string name="settings_manage_calendar_colors_off">Kolory kalendarze nie są ustawiane przez DAVdroid</string>
<string name="settings_event_colors">Obsługa kolorów wydarzeń</string>
<string name="settings_event_colors_on">Synchronizuj kolorów zdarzeń</string>
<string name="settings_event_colors_off">Nie synchronizuj kolorów zdarzeń</string>
<string name="settings_event_colors_off_confirm">Wyłączenie kolorów zdarzeń może usunąć już zsynchronizowane kolory zdarzeń.</string>
<!--collection management-->
<string name="create_addressbook">Stwórz książkę adresową</string>
<string name="create_addressbook_display_name_hint">Moja książka adresowa</string>

View File

@@ -46,6 +46,7 @@
<string name="navigation_drawer_external_links">Links externos</string>
<string name="navigation_drawer_website">Site na Web</string>
<string name="navigation_drawer_faq">Perguntas fequentes</string>
<string name="navigation_drawer_forums">Ajuda / Fóruns</string>
<string name="navigation_drawer_donate">Doações</string>
<string name="account_list_empty">Bem-vindo ao DAVdroid!\n\nVocê pode adicionar uma conta CalDAV/CardDAV agora.</string>
<string name="accounts_global_sync_disabled">A sincronização automática pelo sistema está desativada</string>
@@ -165,6 +166,10 @@
<string name="settings_sync_wifi_only">Sincronizar apenas por Wi-Fi</string>
<string name="settings_sync_wifi_only_on">Sincronização restrita a conexões Wi-Fi</string>
<string name="settings_sync_wifi_only_off">O tipo de conexão não é considerado</string>
<string name="settings_sync_wifi_only_ssids">Restrição de WiFi SSID</string>
<string name="settings_sync_wifi_only_ssids_on">Sincronizar apenas com %s</string>
<string name="settings_sync_wifi_only_ssids_off">Todas as conexões WiFi serão usadas</string>
<string name="settings_sync_wifi_only_ssids_message">Nomes separados por vírgula (SSIDs) das redes WiFi (deixe em branco para todas)</string>
<string name="settings_carddav">CardDAV</string>
<string name="settings_contact_group_method">Método do grupo Contato</string>
<string-array name="settings_contact_group_method_values">
@@ -186,6 +191,10 @@
<string name="settings_manage_calendar_colors">Gerenciar cores dos calendários</string>
<string name="settings_manage_calendar_colors_on">Cores dos calendários definidas pelo DAVdroid</string>
<string name="settings_manage_calendar_colors_off">Cores dos calendários não definidas pelo DAVdroid</string>
<string name="settings_event_colors">Suporte para cor de evento</string>
<string name="settings_event_colors_on">Sincronizar cores de eventos</string>
<string name="settings_event_colors_off">Não sincronizar cores de eventos</string>
<string name="settings_event_colors_off_confirm">Desativar as cores de eventos poderá remover as que já foram sincronizadas</string>
<!--collection management-->
<string name="create_addressbook">Criar livro de endereços</string>
<string name="create_addressbook_display_name_hint">Meu livro de endereços</string>

View File

@@ -0,0 +1,232 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources xmlns:tools="http://schemas.android.com/tools">
<!--common strings-->
<string name="app_name">DAVdroid</string>
<string name="account_title_address_book">DAVdroid 通訊錄</string>
<string name="address_books_authority_title">通訊錄</string>
<string name="help">幫助</string>
<string name="manage_accounts">管理帳號</string>
<string name="please_wait">請稍待 ...</string>
<string name="send">送出</string>
<string name="homepage_url">https://davdroid.bitfire.at/?pk_campaign=davdroid-app</string>
<!--startup dialogs-->
<string name="startup_battery_optimization">電池最佳化</string>
<string name="startup_battery_optimization_message">Android 在數日之後可能會關閉或減少 DAVdroid 的同步。為了避免這發生,請關閉電池最佳化。</string>
<string name="startup_battery_optimization_disable">關閉 DAVdroid 的電池最佳化</string>
<string name="startup_dont_show_again">不要再顯示此訊息</string>
<string name="startup_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商店數位權利管理錯誤訊息</string>
<string name="startup_google_play_accounts_removed_message">在某些情況下Play商店 的數位權利管理可能導致在重開機後或更新 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 這個 app 不存在您的裝置上,所以 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">新聞 &amp; 更新</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_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">用 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">用網址和帳號登入</string>
<string name="login_url_must_be_http_or_https">網址開頭必須是 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">使用 Email 地址當作裝置上的帳號顯示名稱因為當您在行事曆創建活動時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_names">
<item>只手動</item>
<item>每 5 分鐘</item>
<item>每 10 分鐘</item>
<item>每 15 分鐘</item>
<item>每 1 小時</item>
<item>每 2 小時</item>
<item>每 4 小時</item>
<item>每 1 天</item>
</string-array>
<string name="settings_sync_wifi_only">只用 WiFi 同步</string>
<string name="settings_sync_wifi_only_on">只於 WiFi 連線時同步</string>
<string name="settings_sync_wifi_only_off">任何網路連線都可使用</string>
<string name="settings_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>群組存成額外的 VCard 檔案</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">建立行事曆</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">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>

View File

@@ -63,6 +63,11 @@ class AccountSettings @Throws(InvalidAccountException::class) constructor(
"0" false */
val KEY_MANAGE_CALENDAR_COLORS = "manage_calendar_colors"
/* Whether DAVdroid populates and uses CalendarContract.Colors
value = null (not existing) false (default)
"1" true */
val KEY_EVENT_COLORS = "event_colors"
/** Contact group method:
value = null (not existing) groups as separate VCards (default)
"CATEGORIES" groups are per-contact CATEGORIES
@@ -158,6 +163,9 @@ class AccountSettings @Throws(InvalidAccountException::class) constructor(
fun setManageCalendarColors(manage: Boolean) =
accountManager.setUserData(account, KEY_MANAGE_CALENDAR_COLORS, if (manage) null else "0")
fun getEventColors() = accountManager.getUserData(account, KEY_EVENT_COLORS) != null
fun setEventColors(useColors: Boolean) =
accountManager.setUserData(account, KEY_EVENT_COLORS, if (useColors) "1" else null)
// CardDAV settings

View File

@@ -54,7 +54,6 @@ class App: Application() {
override fun onCreate() {
super.onCreate()
CustomCertificates.reinitCertManager(this)
Logger.reinitLogger(this)
addressBookAccountType = getString(R.string.account_type_address_book)

View File

@@ -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)
}
}
}
}

View File

@@ -241,103 +241,107 @@ class DavService: Service() {
Logger.log.info("Refreshing $serviceType collections of service #$service")
// create authenticating OkHttpClient (credentials taken from account settings)
val httpClient = HttpClient.create(this@DavService, account)
HttpClient.Builder(this@DavService, account)
.setForeground(true)
.build().use { client ->
val httpClient = client.okHttpClient
// refresh home set list (from principal)
readPrincipal()?.let { principalUrl ->
Logger.log.fine("Querying principal $principalUrl for home sets")
val principal = DavResource(httpClient, principalUrl)
queryHomeSets(principal)
// refresh home set list (from principal)
readPrincipal()?.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: 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)
// 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)
}
}
}
}
}
}
// remember selected collections
val selectedCollections = HashSet<HttpUrl>()
collections.values
.filter { it.selected }
.forEach { (url,_) -> HttpUrl.parse(url)?.let { selectedCollections.add(it) } }
// 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)
val itHomeSets = homeSets.iterator()
while (itHomeSets.hasNext()) {
val homeSetUrl = itHomeSets.next()
Logger.log.fine("Listing home set $homeSetUrl")
// now refresh collections (taken from home sets)
val itHomeSets = homeSets.iterator()
while (itHomeSets.hasNext()) {
val homeSetUrl = 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)
info.confirmed = true
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
}
} catch(e: HttpException) {
if (e.status in arrayOf(403, 404, 410))
// delete home set only if it was not accessible (40x)
itHomeSets.remove()
}
}
// check/refresh unconfirmed collections
val itCollections = collections.entries.iterator()
while (itCollections.hasNext()) {
val (url, info) = itCollections.next()
if (!info.confirmed)
val homeSet = DavResource(httpClient, homeSetUrl)
try {
val collection = DavResource(httpClient, url)
collection.propfind(0, *CollectionInfo.DAV_PROPERTIES)
val info = CollectionInfo(collection)
info.confirmed = true
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)
info.confirmed = true
Logger.log.log(Level.FINE, "Found collection", info)
// 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()
if ((serviceType == Services.SERVICE_CARDDAV && info.type == CollectionInfo.Type.ADDRESS_BOOK) ||
(serviceType == Services.SERVICE_CALDAV && info.type == CollectionInfo.Type.CALENDAR))
collections[member.location] = info
}
} catch(e: HttpException) {
if (e.status in arrayOf(403, 404, 410))
// delete collection only if it was not accessible (40x)
itCollections.remove()
else
throw e
// delete home set only if it was not accessible (40x)
itHomeSets.remove()
}
}
}
// restore selections
for (url in selectedCollections)
collections[url]?.let { it.selected = true }
// check/refresh unconfirmed collections
val itCollections = collections.entries.iterator()
while (itCollections.hasNext()) {
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
// 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()
} catch(e: HttpException) {
if (e.status 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.beginTransactionNonExclusive()
try {

View File

@@ -11,6 +11,7 @@ package at.bitfire.davdroid;
import android.accounts.Account
import android.content.Context
import android.os.Build
import at.bitfire.cert4android.CustomCertManager
import at.bitfire.dav4android.BasicDigestAuthHandler
import at.bitfire.dav4android.UrlUtils
import at.bitfire.davdroid.log.Logger
@@ -19,7 +20,9 @@ import at.bitfire.davdroid.model.Settings
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import okhttp3.internal.tls.OkHostnameVerifier
import okhttp3.logging.HttpLoggingInterceptor
import java.io.Closeable
import java.net.InetSocketAddress
import java.net.Proxy
import java.text.SimpleDateFormat
@@ -27,12 +30,113 @@ import java.util.*
import java.util.concurrent.TimeUnit
import java.util.logging.Level
class HttpClient private constructor() {
class HttpClient private constructor(
val okHttpClient: OkHttpClient,
private val certManager: CustomCertManager?
): Closeable {
companion object {
override fun close() {
certManager?.close()
}
private val client = OkHttpClient()
private val userAgentInterceptor = UserAgentInterceptor()
class Builder @JvmOverloads constructor(
val context: Context? = null,
accountSettings: AccountSettings? = null,
logger: java.util.logging.Logger = Logger.log
) {
var certManager: CustomCertManager? = null
private val orig = OkHttpClient.Builder()
init {
// set timeouts
orig.connectTimeout(30, TimeUnit.SECONDS)
orig.writeTimeout(30, TimeUnit.SECONDS)
orig.readTimeout(120, TimeUnit.SECONDS)
// don't allow redirects by default, because it would break PROPFIND handling
orig.followRedirects(false)
// add User-Agent to every request
orig.addNetworkInterceptor(UserAgentInterceptor)
// add cookie store for non-persistent cookies (some services like Horde use cookies for session tracking)
orig.cookieJar(MemoryCookieStore())
// add network logging, if requested
if (logger.isLoggable(Level.FINEST)) {
val loggingInterceptor = HttpLoggingInterceptor(HttpLoggingInterceptor.Logger {
message -> logger.finest(message)
})
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
orig.addInterceptor(loggingInterceptor)
}
context?.let {
ServiceDB.OpenHelper(context).use { dbHelper ->
val settings = Settings(dbHelper.readableDatabase)
// custom proxy support
try {
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)
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)
}
// use cert4android to manage self-signed certificates
if (BuildConfig.customCerts)
customCertManager(CustomCertManager(context, !settings.getBoolean(App.DISTRUST_SYSTEM_CERTIFICATES, false)))
}
}
// use account settings for authentication
accountSettings?.let {
val userName = it.username()
val password = it.password()
if (userName != null && password != null)
addAuthentication(null, userName, password)
}
}
constructor(context: Context, account: Account): this(context, AccountSettings(context, account))
constructor(context: Context, host: String?, username: String, password: String): this(context) {
addAuthentication(host, username, password)
}
fun followRedirects(follow: Boolean) { orig.followRedirects(follow) }
fun customCertManager(manager: CustomCertManager) {
certManager = manager
orig.sslSocketFactory(SSLSocketFactoryCompat(manager), manager)
orig.hostnameVerifier(manager.hostnameVerifier(OkHostnameVerifier.INSTANCE))
}
fun setForeground(foreground: Boolean): Builder {
certManager?.appInForeground = foreground
return this
}
fun addAuthentication(host: String?, username: String, password: String): Builder {
val authHandler = BasicDigestAuthHandler(UrlUtils.hostToDomain(host), username, password)
orig .addNetworkInterceptor(authHandler)
.authenticator(authHandler)
return this
}
fun build() = HttpClient(orig.build(), certManager)
}
private object UserAgentInterceptor: Interceptor {
private val productName = when(BuildConfig.FLAVOR) {
App.FLAVOR_ICLOUD -> "MultiSync for Cloud"
@@ -42,104 +146,6 @@ class HttpClient private constructor() {
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}"
@JvmStatic
@JvmOverloads
fun create(context: Context?, settings: AccountSettings? = null, logger: java.util.logging.Logger = Logger.log): OkHttpClient {
var builder = defaultBuilder(context, logger)
// 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)
}
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)
// add cookie store for non-persistent cookies (some services like Horde use cookies for session tracking)
builder.cookieJar(MemoryCookieStore())
// add network logging, if requested
if (logger.isLoggable(Level.FINEST)) {
val loggingInterceptor = HttpLoggingInterceptor(HttpLoggingInterceptor.Logger {
message -> logger.finest(message)
})
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
builder.addInterceptor(loggingInterceptor)
}
return builder
}
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)
}
@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()
}
}
private class UserAgentInterceptor: Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val locale = Locale.getDefault()
val request = chain.request().newBuilder()

View File

@@ -32,14 +32,11 @@ object Logger {
@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")
}
fun reinitLogger(context: Context) {
ServiceDB.OpenHelper(context).use { dbHelper ->
val settings = Settings(dbHelper.getReadableDatabase())
val settings = Settings(dbHelper.readableDatabase)
val logToFile = settings.getBoolean(App.LOG_TO_EXTERNAL_STORAGE, false)
val logVerbose = logToFile || Log.isLoggable(log.name, Log.DEBUG)
@@ -72,7 +69,7 @@ object Logger {
val fileHandler = FileHandler(fileName)
fileHandler.formatter = PlainTextFormatter.DEFAULT
log.addHandler(fileHandler)
rootLogger.addHandler(fileHandler)
val prefIntent = Intent(context, AppSettingsActivity::class.java)
prefIntent.putExtra(AppSettingsActivity.EXTRA_SCROLL_TO, "log_to_external_storage")
@@ -86,7 +83,7 @@ object Logger {
.bigText(context.getString(R.string.logging_to_external_storage, dir.path)))
.setOngoing(true)
} catch (e: IOException) {
} 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))

View File

@@ -29,11 +29,9 @@ class PlainTextFormatter private constructor(
builder .append(DateFormatUtils.format(r.millis, "yyyy-MM-dd HH:mm:ss"))
.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)
@@ -54,8 +52,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("\\$.*$"), "")
}

View File

@@ -9,11 +9,10 @@
package at.bitfire.davdroid.model
import android.content.BroadcastReceiver
import android.content.ContentValues;
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(
@@ -54,6 +53,23 @@ class Settings(
}
fun getLong(name: String, defaultValue: Long): Long {
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.getLong(0)
}
return defaultValue
}
fun putLong(name: String, value: Long?) {
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 ->
@@ -83,9 +99,7 @@ class Settings(
}
override fun onReceive(context: Context, intent: Intent) {
Logger.log.info("Received broadcast: re-initializing settings (logger/cert manager)")
CustomCertificates.reinitCertManager(context)
Logger.log.info("Received broadcast: re-initializing settings (logger)")
Logger.reinitLogger(context)
}

View File

@@ -58,9 +58,6 @@ class LocalCalendar private constructor(
values.put(Calendars.SYNC_EVENTS, 1)
val uri = create(account, provider, values)
// create calendar colors
AndroidCalendar.insertColors(provider, account)
return uri
}

View File

@@ -53,7 +53,6 @@ class CalendarSyncManager(
val MAX_MULTIGET = 20
init {
localCollection = localCalendar
}
@@ -65,7 +64,7 @@ class CalendarSyncManager(
override fun prepare(): Boolean {
collectionURL = HttpUrl.parse(localCalendar.name ?: return false) ?: return false
davCollection = DavCalendar(httpClient, collectionURL)
davCollection = DavCalendar(httpClient.okHttpClient, collectionURL)
return true
}

View File

@@ -41,12 +41,18 @@ class CalendarsSyncAdapterService: SyncAdapterService() {
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(settings))
return
if (settings.getEventColors())
AndroidCalendar.insertColors(provider, account)
else
AndroidCalendar.removeColors(provider, account)
updateLocalCalendars(provider, account, settings)
for (calendar in AndroidCalendar.find(account, provider, LocalCalendar.Factory, "${CalendarContract.Calendars.SYNC_EVENTS}!=0", null)) {
Logger.log.info("Synchronizing calendar #${calendar.id}, URL: ${calendar.name}")
CalendarSyncManager(context, account, settings, extras, authority, syncResult, provider, calendar)
.performSync()
CalendarSyncManager(context, account, settings, extras, authority, syncResult, provider, calendar).use {
it.performSync()
}
}
} catch(e: Exception) {
Logger.log.log(Level.SEVERE, "Couldn't sync calendars", e)

View File

@@ -39,8 +39,9 @@ class ContactsSyncAdapterService: SyncAdapterService() {
Logger.log.info("Synchronizing address book: ${addressBook.getURL()}")
Logger.log.info("Taking settings from: ${addressBook.getMainAccount()}")
ContactsSyncManager(context, account, settings, extras, authority, syncResult, provider, addressBook)
.performSync()
ContactsSyncManager(context, account, settings, extras, authority, syncResult, provider, addressBook).use {
it.performSync()
}
} catch(e: Exception) {
Logger.log.log(Level.SEVERE, "Couldn't sync contacts", e)
}

View File

@@ -120,7 +120,7 @@ class ContactsSyncManager(
localAddressBook.updateSettings(values)
collectionURL = HttpUrl.parse(localAddressBook.getURL()) ?: return false
davCollection = DavAddressBook(httpClient, collectionURL)
davCollection = DavAddressBook(httpClient.okHttpClient, collectionURL)
return true
}
@@ -425,21 +425,20 @@ class ContactsSyncManager(
return null
}
var resourceClient = HttpClient.create(context)
// authenticate only against a certain host, and only upon request
val username = settings.username()
val password = settings.password()
if (username != null && password != null)
resourceClient = HttpClient.addAuthentication(resourceClient, baseUrl.host(), username, password)
val builder = if (username != null && password != null)
HttpClient.Builder(context, baseUrl.host(), username, password)
else
HttpClient.Builder(context)
// allow redirects
resourceClient = resourceClient.newBuilder()
.followRedirects(true)
.build()
builder.followRedirects(true)
val client = builder.build()
try {
val response = resourceClient.newCall(Request.Builder()
val response = client.okHttpClient.newCall(Request.Builder()
.get()
.url(httpUrl)
.build()).execute()
@@ -450,6 +449,8 @@ class ContactsSyncManager(
Logger.log.warning("Couldn't download external resource")
} catch(e: IOException) {
Logger.log.log(Level.SEVERE, "Couldn't download external resource", e)
} finally {
client.close()
}
return null
}

View File

@@ -34,6 +34,7 @@ import at.bitfire.ical4android.CalendarStorageException
import at.bitfire.vcard4android.ContactsStorageException
import okhttp3.HttpUrl
import okhttp3.RequestBody
import java.io.Closeable
import java.io.IOException
import java.util.*
import java.util.logging.Level
@@ -46,7 +47,7 @@ abstract class SyncManager(
val authority: String,
val syncResult: SyncResult,
val uniqueCollectionId: String
) {
): Closeable {
val SYNC_PHASE_PREPARE = 0
val SYNC_PHASE_QUERY_CAPABILITIES = 1
@@ -65,7 +66,7 @@ abstract class SyncManager(
protected lateinit var localCollection: LocalCollection<*>
protected val httpClient = HttpClient.create(context, settings)
protected val httpClient = HttpClient.Builder(context, settings).build()
protected lateinit var collectionURL: HttpUrl
protected lateinit var davCollection: DavResource
@@ -235,6 +236,10 @@ abstract class SyncManager(
}
}
override fun close() {
httpClient.close()
}
/** Prepares synchronization (for instance, allocates necessary resources).
* @return whether actual synchronization is required / can be made. true = synchronization
@@ -261,7 +266,7 @@ abstract class SyncManager(
if (fileName != null) {
Logger.log.info("$fileName has been deleted locally -> deleting from server")
val remote = DavResource(httpClient, collectionURL.newBuilder().addPathSegment(fileName).build())
val remote = DavResource(httpClient.okHttpClient, collectionURL.newBuilder().addPathSegment(fileName).build())
currentDavResource = remote
try {
remote.delete(local.eTag)
@@ -306,7 +311,7 @@ abstract class SyncManager(
currentLocalResource = local
val fileName = local.fileName
val remote = DavResource(httpClient, collectionURL.newBuilder().addPathSegment(fileName).build())
val remote = DavResource(httpClient.okHttpClient, collectionURL.newBuilder().addPathSegment(fileName).build())
currentDavResource = remote
// generate entity to upload (VCard, iCal, whatever)

View File

@@ -50,8 +50,9 @@ class TasksSyncAdapterService: SyncAdapterService() {
for (taskList in AndroidTaskList.find(account, taskProvider, LocalTaskList.Factory, "${TaskContract.TaskLists.SYNC_ENABLED}!=0", null)) {
Logger.log.info("Synchronizing task list #${taskList.id} [${taskList.syncId}]")
TasksSyncManager(context, account, settings, extras, authority, syncResult, taskProvider, taskList)
.performSync()
TasksSyncManager(context, account, settings, extras, authority, syncResult, taskProvider, taskList).use {
it.performSync()
}
}
} catch (e: Exception) {
Logger.log.log(Level.SEVERE, "Couldn't sync task lists", e)

View File

@@ -62,7 +62,7 @@ class TasksSyncManager(
override fun prepare(): Boolean {
collectionURL = HttpUrl.parse(localTaskList.syncId ?: return false) ?: return false
davCollection = DavCalendar(httpClient, collectionURL)
davCollection = DavCalendar(httpClient.okHttpClient, collectionURL)
return true
}

View File

@@ -10,7 +10,6 @@ package at.bitfire.davdroid.ui
import android.accounts.Account
import android.accounts.AccountManager
import android.app.AlertDialog
import android.app.Dialog
import android.app.LoaderManager
import android.content.*
@@ -23,12 +22,12 @@ import android.provider.CalendarContract
import android.provider.ContactsContract
import android.support.design.widget.Snackbar
import android.support.v4.app.DialogFragment
import android.support.v7.app.AlertDialog
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.view.*
import android.widget.*
import at.bitfire.davdroid.App
import at.bitfire.davdroid.CustomCertificates
import at.bitfire.davdroid.DavService
import at.bitfire.davdroid.R
import at.bitfire.davdroid.log.Logger
@@ -40,6 +39,7 @@ import at.bitfire.davdroid.resource.LocalAddressBook
import at.bitfire.davdroid.resource.LocalTaskList
import at.bitfire.ical4android.TaskProvider
import at.bitfire.vcard4android.ContactsStorageException
import kotlinx.android.synthetic.main.account_caldav_item.view.*
import kotlinx.android.synthetic.main.activity_account.*
import java.util.*
import java.util.logging.Level
@@ -97,16 +97,6 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop
loaderManager.initLoader(0, intent.extras, this)
}
override fun onPause() {
super.onPause()
CustomCertificates.certManager?.let { it.appInForeground = false }
}
override fun onResume() {
super.onResume()
CustomCertificates.certManager?.let { it.appInForeground = true }
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.activity_account, menu)
return true
@@ -178,11 +168,13 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop
}
private val onItemClickListener = AdapterView.OnItemClickListener { parent, _, position, _ ->
private val onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, _ ->
if (!view.isEnabled)
return@OnItemClickListener
val list = parent as ListView
val adapter = list.adapter as ArrayAdapter<CollectionInfo>
val info = adapter.getItem(position)
val nowChecked = !info.selected
OpenHelper(this@AccountActivity).use { dbHelper ->
@@ -406,7 +398,6 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop
class AddressBookAdapter(
context: Context
): ArrayAdapter<CollectionInfo>(context, R.layout.account_carddav_item) {
override fun getView(position: Int, v: View?, parent: ViewGroup?): View {
val v = v ?: LayoutInflater.from(context).inflate(R.layout.account_carddav_item, parent, false)
val info = getItem(position)
@@ -435,11 +426,14 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop
class CalendarAdapter(
context: Context
): ArrayAdapter<CollectionInfo>(context, R.layout.account_caldav_item) {
override fun getView(position: Int, v: View?, parent: ViewGroup?): View {
val v = v ?: LayoutInflater.from(context).inflate(R.layout.account_caldav_item, parent, false)
val info = getItem(position)
val enabled = info.selected || info.supportsVEVENT || info.supportsVTODO
v.isEnabled = enabled
v.checked.isEnabled = enabled
val checked: CheckBox = v.findViewById(R.id.checked)
checked.isChecked = info.selected

View File

@@ -20,6 +20,7 @@ import android.support.v4.app.LoaderManager
import android.support.v4.app.NavUtils
import android.support.v4.content.AsyncTaskLoader
import android.support.v4.content.Loader
import android.support.v7.app.AlertDialog
import android.support.v7.app.AppCompatActivity
import android.support.v7.preference.*
import android.support.v7.preference.Preference.OnPreferenceChangeListener
@@ -227,6 +228,26 @@ class AccountSettingsActivity: AppCompatActivity() {
} else
prefManageColors.isEnabled = false
val prefEventColors = findPreference("event_colors") as SwitchPreferenceCompat
prefEventColors.isChecked = settings.getEventColors()
prefEventColors.onPreferenceChangeListener = OnPreferenceChangeListener { _, newValue ->
if (newValue as Boolean) {
settings.setEventColors(true)
loaderManager.restartLoader(0, arguments, this)
} else
AlertDialog.Builder(activity)
.setIcon(R.drawable.ic_error_dark)
.setTitle(R.string.settings_event_colors)
.setMessage(R.string.settings_event_colors_off_confirm)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok, { _, _ ->
settings.setEventColors(false)
loaderManager.restartLoader(0, arguments, this)
})
.show()
false
}
}
override fun onLoaderReset(loader: Loader<AccountSettings>) {

View File

@@ -16,8 +16,9 @@ import android.support.v7.preference.EditTextPreference
import android.support.v7.preference.Preference
import android.support.v7.preference.PreferenceFragmentCompat
import android.support.v7.preference.SwitchPreferenceCompat
import at.bitfire.cert4android.CustomCertManager
import at.bitfire.davdroid.App
import at.bitfire.davdroid.CustomCertificates
import at.bitfire.davdroid.BuildConfig
import at.bitfire.davdroid.R
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.model.ServiceDB
@@ -112,17 +113,17 @@ class AppSettingsActivity: AppCompatActivity() {
}
val prefDistrustSystemCerts = findPreference("distrust_system_certs") as SwitchPreferenceCompat
if (CustomCertificates.certManager == null)
prefDistrustSystemCerts.isVisible = false
else
if (BuildConfig.customCerts)
prefDistrustSystemCerts.isChecked = settings.getBoolean(App.DISTRUST_SYSTEM_CERTIFICATES, false)
else
prefDistrustSystemCerts.isVisible = false
prefDistrustSystemCerts.onPreferenceClickListener = Preference.OnPreferenceClickListener {
setDistrustSystemCerts(prefDistrustSystemCerts.isChecked)
true
}
val prefResetCertificates = findPreference("reset_certificates")
if (CustomCertificates.certManager == null)
if (!BuildConfig.customCerts)
prefResetCertificates.isVisible = false
prefResetCertificates.onPreferenceClickListener = Preference.OnPreferenceClickListener {
resetCertificates()
@@ -148,17 +149,11 @@ class AppSettingsActivity: AppCompatActivity() {
private fun setDistrustSystemCerts(distrust: Boolean) {
settings.putBoolean(App.DISTRUST_SYSTEM_CERTIFICATES, distrust)
// re-initialize certificate manager
CustomCertificates.reinitCertManager(context)
// reinitialize certificate manager of :sync process
context.sendBroadcast(Intent(Settings.ReinitSettingsReceiver.ACTION_REINIT_SETTINGS))
}
private fun resetCertificates() {
CustomCertificates.certManager?.resetCertificates()
Snackbar.make(view!!, getString(R.string.app_settings_reset_certificates_success), Snackbar.LENGTH_LONG).show()
if (CustomCertManager.resetCertificates(activity))
Snackbar.make(view!!, getString(R.string.app_settings_reset_certificates_success), Snackbar.LENGTH_LONG).show()
}
private fun setExternalLogging(externalLogging: Boolean) {

View File

@@ -182,9 +182,12 @@ class CreateCollectionFragment: DialogFragment(), LoaderManager.LoaderCallbacks<
Logger.log.log(Level.SEVERE, "Couldn't assemble Extended MKCOL request", e)
}
var httpClient: HttpClient? = null
try {
val client = HttpClient.create(context, account)
val collection = DavResource(client, HttpUrl.parse(info.url)!!)
httpClient = HttpClient.Builder(context, account)
.setForeground(true)
.build()
val collection = DavResource(httpClient.okHttpClient, HttpUrl.parse(info.url)!!)
// create collection on remote server
collection.mkCol(writer.toString())
@@ -214,6 +217,8 @@ class CreateCollectionFragment: DialogFragment(), LoaderManager.LoaderCallbacks<
}
} catch(e: Exception) {
return e
} finally {
httpClient?.close()
}
return null
}

View File

@@ -82,9 +82,12 @@ class DeleteCollectionFragment: DialogFragment(), LoaderManager.LoaderCallbacks<
override fun onStartLoading() = forceLoad()
override fun loadInBackground(): Exception? {
var httpClient: HttpClient? = null
try {
val httpClient = HttpClient.create(context, account)
val collection = DavResource(httpClient, HttpUrl.parse(collectionInfo.url)!!)
httpClient = HttpClient.Builder(context, account)
.setForeground(true)
.build()
val collection = DavResource(httpClient.okHttpClient, HttpUrl.parse(collectionInfo.url)!!)
// delete collection from server
collection.delete(null)
@@ -98,6 +101,8 @@ class DeleteCollectionFragment: DialogFragment(), LoaderManager.LoaderCallbacks<
return null
} catch(e: Exception) {
return e
} finally {
httpClient?.close()
}
}
}

View File

@@ -53,9 +53,9 @@ class PermissionsActivity: AppCompatActivity() {
noTaskPermissions =
ActivityCompat.checkSelfPermission(this, PERMISSION_READ_TASKS) != PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(this, PERMISSION_WRITE_TASKS) != PackageManager.PERMISSION_GRANTED
findViewById(R.id.opentasks_permissions).visibility = if (noTaskPermissions) View.VISIBLE else View.GONE
findViewById<View>(R.id.opentasks_permissions).visibility = if (noTaskPermissions) View.VISIBLE else View.GONE
} else {
findViewById(R.id.opentasks_permissions).visibility = View.GONE;
findViewById<View>(R.id.opentasks_permissions).visibility = View.GONE;
noTaskPermissions = false
}

View File

@@ -38,11 +38,14 @@ class StartupDialogFragment: DialogFragment() {
BATTERY_OPTIMIZATIONS,
DEVELOPMENT_VERSION,
GOOGLE_PLAY_ACCOUNTS_REMOVED,
OPENTASKS_NOT_INSTALLED
OPENTASKS_NOT_INSTALLED,
OSE_DONATE
}
companion object {
private val SETTING_NEXT_DONATION_POPUP = "time_nextDonationPopup"
@JvmField val HINT_BATTERY_OPTIMIZATIONS = "hint_BatteryOptimizations"
@JvmField val HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED = "hint_GooglePlayAccountsRemoved"
@JvmField val HINT_OPENTASKS_NOT_INSTALLED = "hint_OpenTasksNotInstalled"
@@ -57,14 +60,15 @@ class StartupDialogFragment: DialogFragment() {
if (BuildConfig.VERSION_NAME.contains("-alpha") || BuildConfig.VERSION_NAME.contains("-beta") || BuildConfig.VERSION_NAME.contains("-rc"))
dialogs += StartupDialogFragment.instantiate(Mode.DEVELOPMENT_VERSION)
else {
// store-specific information
if (BuildConfig.FLAVOR == App.FLAVOR_GOOGLE_PLAY) {
// Play store
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && // only on Android <5
settings.getBoolean(HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED, true)) // and only when "Don't show again" hasn't been clicked yet
dialogs += StartupDialogFragment.instantiate(Mode.GOOGLE_PLAY_ACCOUNTS_REMOVED)
}
/* else if (System.currentTimeMillis() > settings.getLong(SETTING_NEXT_DONATION_POPUP, 0))
dialogs += StartupDialogFragment.instantiate(Mode.OSE_DONATE) */
// store-specific information
if (BuildConfig.FLAVOR == App.FLAVOR_GOOGLE_PLAY) {
// Play store
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && // only on Android <5
settings.getBoolean(HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED, true)) // and only when "Don't show again" hasn't been clicked yet
dialogs += StartupDialogFragment.instantiate(Mode.GOOGLE_PLAY_ACCOUNTS_REMOVED)
}
// battery optimization white-listing
@@ -97,6 +101,8 @@ class StartupDialogFragment: DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
isCancelable = false
val mode = Mode.valueOf(arguments.getString(ARGS_MODE))
return when (mode) {
Mode.BATTERY_OPTIMIZATIONS ->
@@ -104,8 +110,8 @@ class StartupDialogFragment: DialogFragment() {
.setIcon(R.drawable.ic_info_dark)
.setTitle(R.string.startup_battery_optimization)
.setMessage(R.string.startup_battery_optimization_message)
.setPositiveButton(android.R.string.ok, { _: DialogInterface, _: Int -> })
.setNeutralButton(R.string.startup_battery_optimization_disable, { _: DialogInterface, _: Int ->
.setPositiveButton(android.R.string.ok, { _, _ -> })
.setNeutralButton(R.string.startup_battery_optimization_disable, { _, _ ->
val intent = Intent(android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
Uri.parse("package:" + BuildConfig.APPLICATION_ID))
if (intent.resolveActivity(context.packageManager) != null)
@@ -141,12 +147,12 @@ class StartupDialogFragment: DialogFragment() {
.setIcon(icon)
.setTitle(R.string.startup_google_play_accounts_removed)
.setMessage(R.string.startup_google_play_accounts_removed_message)
.setPositiveButton(android.R.string.ok, { _: DialogInterface, _: Int -> })
.setNeutralButton(R.string.startup_google_play_accounts_removed_more_info, { _: DialogInterface, _: Int ->
.setPositiveButton(android.R.string.ok, { _, _ -> })
.setNeutralButton(R.string.startup_google_play_accounts_removed_more_info, { _, _ ->
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.navigation_drawer_faq_url)))
context.startActivity(intent)
})
.setNegativeButton(R.string.startup_dont_show_again, { _: DialogInterface, _: Int ->
.setNegativeButton(R.string.startup_dont_show_again, { _, _ ->
ServiceDB.OpenHelper(context).use { dbHelper ->
val settings = Settings(dbHelper.writableDatabase)
settings.putBoolean(HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED, false)
@@ -163,8 +169,8 @@ class StartupDialogFragment: DialogFragment() {
.setIcon(R.drawable.ic_alarm_on_dark)
.setTitle(R.string.startup_opentasks_not_installed)
.setMessage(builder.toString())
.setPositiveButton(android.R.string.ok, { _: DialogInterface, _: Int -> })
.setNeutralButton(R.string.startup_opentasks_not_installed_install, { _: DialogInterface, _: Int ->
.setPositiveButton(android.R.string.ok, { _, _ -> })
.setNeutralButton(R.string.startup_opentasks_not_installed_install, { _, _ ->
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=org.dmfs.tasks"))
if (intent.resolveActivity(context.packageManager) != null)
context.startActivity(intent)
@@ -179,6 +185,31 @@ class StartupDialogFragment: DialogFragment() {
})
.create()
}
Mode.OSE_DONATE ->
return AlertDialog.Builder(activity)
.setIcon(R.mipmap.ic_launcher)
.setTitle(R.string.startup_donate)
.setMessage(R.string.startup_donate_message)
.setPositiveButton(R.string.startup_donate_now, { _, _ ->
val uri = Uri.parse(getString(R.string.homepage_url))
.buildUpon()
.appendEncodedPath("donate/")
.build()
startActivity(Intent(Intent.ACTION_VIEW, uri))
ServiceDB.OpenHelper(context).use { dbHelper ->
val settings = Settings(dbHelper.writableDatabase)
settings.putLong(SETTING_NEXT_DONATION_POPUP, System.currentTimeMillis() + 30 * 86400000L) // 30 days
}
})
.setNegativeButton(R.string.startup_donate_later, { _, _ ->
ServiceDB.OpenHelper(context).use { dbHelper ->
val settings = Settings(dbHelper.writableDatabase)
settings.putLong(SETTING_NEXT_DONATION_POPUP, System.currentTimeMillis() + 14 * 86400000L) // 14 days
}
})
.create()
}
}

View File

@@ -17,10 +17,10 @@ import at.bitfire.davdroid.HttpClient
import at.bitfire.davdroid.log.StringHandler
import at.bitfire.davdroid.model.CollectionInfo
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import org.apache.commons.lang3.builder.ReflectionToStringBuilder
import org.apache.commons.lang3.builder.ToStringBuilder
import org.xbill.DNS.*
import java.io.Closeable
import java.io.IOException
import java.io.Serializable
import java.net.URI
@@ -31,8 +31,8 @@ import java.util.logging.Logger
class DavResourceFinder(
val context: Context,
val credentials: LoginCredentials
) {
private val credentials: LoginCredentials
): Closeable {
enum class Service(val wellKnownName: String) {
CALDAV("caldav"),
@@ -42,22 +42,25 @@ class DavResourceFinder(
}
val log = Logger.getLogger("davdroid.DavResourceFinder")!!
val logBuffer = StringHandler()
private val logBuffer = StringHandler()
init {
log.level = Level.FINEST
log.addHandler(logBuffer)
}
val httpClient: OkHttpClient
init {
val builder = HttpClient.create(context, null, log).newBuilder()
httpClient = HttpClient.addAuthentication(builder, null, credentials.userName, credentials.password).build()
}
private val httpClient: HttpClient = HttpClient.Builder(context, null, log)
.addAuthentication(null, credentials.userName, credentials.password)
.setForeground(true)
.build()
fun cancel() {
log.warning("Shutting down resource detection")
httpClient.dispatcher().executorService().shutdownNow()
httpClient.connectionPool().evictAll()
httpClient.okHttpClient.dispatcher().executorService().shutdownNow()
httpClient.okHttpClient.connectionPool().evictAll()
}
override fun close() {
httpClient.close()
}
@@ -120,7 +123,7 @@ class DavResourceFinder(
if (config.principal != null && service == Service.CALDAV) {
// query email address (CalDAV scheduling: calendar-user-address-set)
val davPrincipal = DavResource(httpClient, HttpUrl.get(config.principal)!!, log)
val davPrincipal = DavResource(httpClient.okHttpClient, HttpUrl.get(config.principal)!!, log)
try {
davPrincipal.propfind(0, CalendarUserAddressSet.NAME)
(davPrincipal.properties[CalendarUserAddressSet.NAME] as CalendarUserAddressSet?)?.let { addressSet ->
@@ -151,7 +154,7 @@ class DavResourceFinder(
var principal: HttpUrl? = null
try {
val davBase = DavResource(httpClient, baseURL, log)
val davBase = DavResource(httpClient.okHttpClient, baseURL, log)
when (service) {
Service.CARDDAV -> {
@@ -258,7 +261,7 @@ class DavResourceFinder(
@Throws(IOException::class)
fun providesService(url: HttpUrl, service: Service): Boolean {
val davPrincipal = DavResource(httpClient, url, log)
val davPrincipal = DavResource(httpClient.okHttpClient, url, log)
try {
davPrincipal.options()
@@ -348,7 +351,7 @@ class DavResourceFinder(
*/
@Throws(IOException::class, HttpException::class, DavException::class)
fun getCurrentUserPrincipal(url: HttpUrl, service: Service?): URI? {
val dav = DavResource(httpClient, url, log)
val dav = DavResource(httpClient.okHttpClient, url, log)
dav.propfind(0, CurrentUserPrincipal.NAME)
dav.findProperty(CurrentUserPrincipal.NAME)?.let { (dav, second) ->

View File

@@ -14,7 +14,6 @@ import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.Menu
import android.view.MenuItem
import at.bitfire.davdroid.CustomCertificates
import at.bitfire.davdroid.R
import java.util.*
@@ -59,16 +58,6 @@ class LoginActivity: AppCompatActivity() {
.commit()
}
override fun onResume() {
super.onResume()
CustomCertificates.certManager?.let { it.appInForeground = true }
}
override fun onPause() {
super.onPause()
CustomCertificates.certManager?.let { it.appInForeground = false }
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.activity_login, menu)
return true

View File

@@ -191,6 +191,10 @@
<string name="settings_manage_calendar_colors">Kalenderfarben verwalten</string>
<string name="settings_manage_calendar_colors_on">Kalenderfarben werden von DAVdroid verwaltet</string>
<string name="settings_manage_calendar_colors_off">DAVdroid setzt keine Kalenderfarben</string>
<string name="settings_event_colors">Unterstützung für Terminfarben</string>
<string name="settings_event_colors_on">Terminfarben werden synchronisiert</string>
<string name="settings_event_colors_off">Terminfarben werden nicht synchronisiert</string>
<string name="settings_event_colors_off_confirm">Die Deaktivierung der Terminfarben kann bereits synchronisierte Terminfarben löschen!</string>
<!--collection management-->
<string name="create_addressbook">Adressbuch erstellen</string>
<string name="create_addressbook_display_name_hint">Meine Adressen</string>

View File

@@ -30,7 +30,7 @@
<string name="startup_development_version">Preview Release</string>
<string name="startup_development_version_message">This is a development version of %s. Things may not work as expected. Your feedback is welcome.</string>
<string name="startup_development_version_give_feedback">Give feedback</string>
<string name="startup_development_version_feedback_url">https://davdroid.bitfire.at/forums/?pk_campaign=davdroid-app</string>
<string name="startup_development_version_feedback_url">https://forums.bitfire.at/category/9/beta-test-discussion?pk_campaign=davdroid-app</string>
<string name="startup_donate">Open-Source Information</string>
<string name="startup_donate_message">We\'re happy that you use DAVdroid, which is open-source software (GPLv3). Because developing DAVdroid is hard work and took us thousands of working hours, please consider a donation.</string>
<string name="startup_donate_now">Show donation page</string>
@@ -220,6 +220,10 @@
<string name="settings_manage_calendar_colors">Manage calendar colors</string>
<string name="settings_manage_calendar_colors_on">Calendar colors are managed by DAVdroid</string>
<string name="settings_manage_calendar_colors_off">Calendar colors are not set by DAVdroid</string>
<string name="settings_event_colors">Event color support</string>
<string name="settings_event_colors_on">Sync event colors</string>
<string name="settings_event_colors_off">Do not sync event colors</string>
<string name="settings_event_colors_off_confirm">Turning off event colors may remove already synchronized event colors.</string>
<!-- collection management -->
<string name="create_addressbook">Create address book</string>

View File

@@ -94,6 +94,13 @@
android:summaryOn="@string/settings_manage_calendar_colors_on"
android:summaryOff="@string/settings_manage_calendar_colors_off"/>
<SwitchPreferenceCompat
android:key="event_colors"
android:persistent="false"
android:title="@string/settings_event_colors"
android:summaryOn="@string/settings_event_colors_on"
android:summaryOff="@string/settings_event_colors_off"/>
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -27,11 +27,11 @@ import static org.junit.Assert.assertNull;
public class HttpClientTest {
MockWebServer server;
OkHttpClient httpClient;
HttpClient httpClient;
@Before
public void setUp() throws IOException {
httpClient = HttpClient.create(null);
httpClient = new HttpClient.Builder().build();
server = new MockWebServer();
server.start(30000);
@@ -40,6 +40,7 @@ public class HttpClientTest {
@After
public void tearDown() throws IOException {
server.shutdown();
httpClient.close();
}
@@ -53,7 +54,7 @@ public class HttpClientTest {
.addHeader("Set-Cookie", "cookie1=1; path=/")
.addHeader("Set-Cookie", "cookie2=2")
.setBody("Cookie set"));
httpClient.newCall(new Request.Builder()
httpClient.getOkHttpClient().newCall(new Request.Builder()
.get().url(url)
.build()).execute();
assertNull(server.takeRequest().getHeader("Cookie"));
@@ -64,14 +65,14 @@ public class HttpClientTest {
.addHeader("Set-Cookie", "cookie1=1a; path=/; Max-Age=0")
.addHeader("Set-Cookie", "cookie2=2a")
.setResponseCode(200));
httpClient.newCall(new Request.Builder()
httpClient.getOkHttpClient().newCall(new Request.Builder()
.get().url(url)
.build()).execute();
assertEquals("cookie2=2; cookie1=1", server.takeRequest().getHeader("Cookie"));
server.enqueue(new MockResponse()
.setResponseCode(200));
httpClient.newCall(new Request.Builder()
httpClient.getOkHttpClient().newCall(new Request.Builder()
.get().url(url)
.build()).execute();
assertEquals("cookie2=2a", server.takeRequest().getHeader("Cookie"));

View File

@@ -8,7 +8,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.1.4'
ext.kotlin_version = '1.1.4-3'
repositories {
jcenter()
@@ -22,6 +22,9 @@ buildscript {
allprojects {
repositories {
jcenter()
maven {
url "https://maven.google.com"
}
}
}

View File

@@ -1,7 +1,7 @@
#!/bin/bash
declare -A android
android=([ca]=ca [cs]=cs [da]=da [de]=de [es]=es [fr]=fr [hu]=hu [it]=it [ja]=ja [nl]=nl [pl]=pl [pt]=pt [pt_BR]=pt-rBR [ru]=ru [sr]=sr [tr_TR]=tr-rTR [uk]=uk [zh_CN]=zh-rCN)
android=([ca]=ca [cs]=cs [da]=da [de]=de [es]=es [fr]=fr [hu]=hu [it]=it [ja]=ja [nl]=nl [nb_NO]=nb-rNO [pl]=pl [pt]=pt [pt_BR]=pt-rBR [ru]=ru [sr]=sr [tr_TR]=tr-rTR [uk]=uk [zh_CN]=zh-rCN [zh_TW]=zh-rTW)
for lang in ${!android[@]}
do