mirror of
https://github.com/bitfireAT/davx5-ose.git
synced 2026-01-06 05:47:50 -05:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19b54748cd | ||
|
|
41ce609237 | ||
|
|
aafcc36c4d | ||
|
|
2496a3bf05 | ||
|
|
54e6426dc4 | ||
|
|
4542da7d89 | ||
|
|
977409511a | ||
|
|
ad8c832819 | ||
|
|
389af2b738 | ||
|
|
be2e15e463 | ||
|
|
c7c13520f9 | ||
|
|
317144630c | ||
|
|
34bc27fa79 | ||
|
|
210735a500 | ||
|
|
b30733c64b | ||
|
|
91234a688f | ||
|
|
5675e544b5 | ||
|
|
42a261b84e | ||
|
|
0d1825cbf3 | ||
|
|
9b8fc983cd | ||
|
|
3bde3758fc | ||
|
|
fd1f59d124 | ||
|
|
9886507b7d | ||
|
|
144643d6af | ||
|
|
14875f63ea | ||
|
|
28e567cf78 | ||
|
|
7997606550 | ||
|
|
fb0552de46 | ||
|
|
03c15a6924 | ||
|
|
c3b2929f88 | ||
|
|
eb2537a278 | ||
|
|
0b9727cca6 | ||
|
|
61231b4233 | ||
|
|
59252d7471 | ||
|
|
6ffa6fa9a7 | ||
|
|
03ee9a037b | ||
|
|
7ab13d648e | ||
|
|
25c54cce62 | ||
|
|
f0e45c71f5 | ||
|
|
fa528a64e9 | ||
|
|
c6aed90c96 | ||
|
|
2280f899ee |
@@ -15,14 +15,22 @@ android {
|
||||
defaultConfig {
|
||||
applicationId "at.bitfire.davdroid"
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 22
|
||||
targetSdkVersion 23
|
||||
|
||||
versionCode 92
|
||||
versionName "1.0.2"
|
||||
versionCode 109
|
||||
|
||||
buildConfigField "long", "buildTime", System.currentTimeMillis() + "L"
|
||||
}
|
||||
|
||||
productFlavors {
|
||||
standard {
|
||||
versionName "1.2"
|
||||
}
|
||||
gplay {
|
||||
versionName "1.2-gplay"
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
minifyEnabled false
|
||||
@@ -40,8 +48,9 @@ android {
|
||||
disable 'IconColors'
|
||||
disable 'IconLauncherShape'
|
||||
disable 'IconMissingDensityFolder'
|
||||
disable 'ImpliedQuantity'
|
||||
disable 'MissingTranslation'
|
||||
disable 'OldTargetApi' // Android 6 permission model not implemented yet
|
||||
disable 'MissingQuantity'
|
||||
disable 'Recycle' // doesn't understand Lombok's @Cleanup
|
||||
disable 'RtlEnabled'
|
||||
disable 'RtlHardcoded'
|
||||
@@ -54,13 +63,7 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
exclude module: 'commons-logging' // undocumented part of Android
|
||||
}
|
||||
|
||||
dependencies {
|
||||
provided 'org.projectlombok:lombok:1.16.6'
|
||||
|
||||
compile project(':dav4android')
|
||||
compile project(':ical4android')
|
||||
compile project(':vcard4android')
|
||||
@@ -68,12 +71,18 @@ dependencies {
|
||||
compile 'com.android.support:appcompat-v7:23.+'
|
||||
compile 'com.android.support:cardview-v7:23.+'
|
||||
compile 'com.android.support:design:23.+'
|
||||
compile 'com.android.support:preference-v7:23.+'
|
||||
compile 'com.android.support:preference-v14:23.+'
|
||||
|
||||
compile 'com.github.yukuku:ambilwarna:2.0.1'
|
||||
compile project(':MemorizingTrustManager')
|
||||
|
||||
androidTestCompile 'com.squareup.okhttp3:mockwebserver:3.2.0'
|
||||
compile 'dnsjava:dnsjava:2.1.7'
|
||||
compile 'org.apache.commons:commons-lang3:3.4'
|
||||
compile 'org.apache.commons:commons-collections4:4.1'
|
||||
provided 'org.projectlombok:lombok:1.16.8'
|
||||
|
||||
// for tests
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile 'com.squareup.okhttp3:mockwebserver:3.3.1'
|
||||
androidTestCompile 'com.squareup.okhttp3:mockwebserver:3.3.1'
|
||||
}
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
|
||||
# ProGuard usage for DAVdroid:
|
||||
# shrinking yes - main reason for using ProGuard
|
||||
# optimization no - too risky
|
||||
# obfuscation no - DAVdroid is open-source
|
||||
# preverification no (Android default)
|
||||
# shrinking yes (main reason for using ProGuard)
|
||||
# optimization yes
|
||||
# obfuscation no (DAVdroid is open-source)
|
||||
# preverification no
|
||||
|
||||
-dontobfuscate
|
||||
|
||||
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
|
||||
-optimizationpasses 5
|
||||
-allowaccessmodification
|
||||
-dontpreverify
|
||||
|
||||
# ez-vcard
|
||||
-dontwarn com.fasterxml.jackson.** # Jackson JSON Processor (for jCards) not used
|
||||
-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
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2013 – 2015 Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
package at.bitfire.davdroid;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
public class ArrayUtilsTest extends TestCase {
|
||||
|
||||
public void testPartition() {
|
||||
// n == 0
|
||||
assertTrue(Arrays.deepEquals(
|
||||
new Long[0][0],
|
||||
ArrayUtils.partition(new Long[] { }, 5)));
|
||||
|
||||
// n < max
|
||||
assertTrue(Arrays.deepEquals(
|
||||
new Long[][] { { 1l, 2l } },
|
||||
ArrayUtils.partition(new Long[] { 1l, 2l }, 5)));
|
||||
|
||||
// n == max
|
||||
assertTrue(Arrays.deepEquals(
|
||||
new Long[][] { { 1l, 2l }, { 3l, 4l } },
|
||||
ArrayUtils.partition(new Long[] { 1l, 2l, 3l, 4l }, 2)));
|
||||
|
||||
// n > max
|
||||
assertTrue(Arrays.deepEquals(
|
||||
new Long[][] { { 1l, 2l, 3l, 4l, 5l }, { 6l, 7l, 8l, 9l, 10l }, { 11l } },
|
||||
ArrayUtils.partition(new Long[] { 1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l, 9l, 10l, 11l }, 5)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,15 +11,12 @@ package at.bitfire.davdroid;
|
||||
import android.os.Build;
|
||||
import android.test.InstrumentationTestCase;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
|
||||
import javax.net.ssl.SSLSocket;
|
||||
|
||||
import de.duenndns.ssl.MemorizingTrustManager;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
|
||||
public class SSLSocketFactoryCompatTest extends InstrumentationTestCase {
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2013 – 2015 Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
public class TestConstants {
|
||||
public static final String ROBOHYDRA_BASE = "http://192.168.0.11:3000/";
|
||||
|
||||
public static URI roboHydra;
|
||||
static {
|
||||
try {
|
||||
roboHydra = new URI(ROBOHYDRA_BASE);
|
||||
} catch(URISyntaxException e) {
|
||||
Log.wtf("davdroid.test.Constants", "Invalid RoboHydra base URL");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.model;
|
||||
|
||||
import android.content.ContentValues;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import at.bitfire.dav4android.DavResource;
|
||||
import at.bitfire.dav4android.exception.DavException;
|
||||
import at.bitfire.dav4android.exception.HttpException;
|
||||
import at.bitfire.dav4android.property.ResourceType;
|
||||
import at.bitfire.davdroid.HttpClient;
|
||||
import at.bitfire.davdroid.model.ServiceDB.Collections;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
|
||||
public class CollectionInfoTest extends TestCase {
|
||||
|
||||
MockWebServer server = new MockWebServer();
|
||||
|
||||
public void testFromDavResource() throws IOException, HttpException, DavException {
|
||||
// r/w address book
|
||||
server.enqueue(new MockResponse()
|
||||
.setResponseCode(207)
|
||||
.setBody("<multistatus xmlns='DAV:' xmlns:CARD='urn:ietf:params:xml:ns:carddav'>" +
|
||||
"<response>" +
|
||||
" <href>/</href>" +
|
||||
" <propstat><prop>" +
|
||||
" <resourcetype><collection/><CARD:addressbook/></resourcetype>" +
|
||||
" <displayname>My Contacts</displayname>" +
|
||||
" <CARD:addressbook-description>My Contacts Description</CARD:addressbook-description>" +
|
||||
" </prop></propstat>" +
|
||||
"</response>" +
|
||||
"</multistatus>"));
|
||||
|
||||
DavResource dav = new DavResource(HttpClient.create(), server.url("/"));
|
||||
dav.propfind(0, ResourceType.NAME);
|
||||
CollectionInfo info = CollectionInfo.fromDavResource(dav);
|
||||
assertEquals(CollectionInfo.Type.ADDRESS_BOOK, info.type);
|
||||
assertFalse(info.readOnly);
|
||||
assertEquals("My Contacts", info.displayName);
|
||||
assertEquals("My Contacts Description", info.description);
|
||||
|
||||
// read-only calendar, no display name
|
||||
server.enqueue(new MockResponse()
|
||||
.setResponseCode(207)
|
||||
.setBody("<multistatus xmlns='DAV:' xmlns:CAL='urn:ietf:params:xml:ns:caldav' xmlns:ICAL='http://apple.com/ns/ical/'>" +
|
||||
"<response>" +
|
||||
" <href>/</href>" +
|
||||
" <propstat><prop>" +
|
||||
" <resourcetype><collection/><CAL:calendar/></resourcetype>" +
|
||||
" <current-user-privilege-set><privilege><read/></privilege></current-user-privilege-set>" +
|
||||
" <CAL:calendar-description>My Calendar</CAL:calendar-description>" +
|
||||
" <CAL:calendar-timezone>tzdata</CAL:calendar-timezone>" +
|
||||
" <ICAL:calendar-color>#ff0000</ICAL:calendar-color>" +
|
||||
" </prop></propstat>" +
|
||||
"</response>" +
|
||||
"</multistatus>"));
|
||||
|
||||
dav = new DavResource(HttpClient.create(), server.url("/"));
|
||||
dav.propfind(0, ResourceType.NAME);
|
||||
info = CollectionInfo.fromDavResource(dav);
|
||||
assertEquals(CollectionInfo.Type.CALENDAR, info.type);
|
||||
assertTrue(info.readOnly);
|
||||
assertNull(info.displayName);
|
||||
assertEquals("My Calendar", info.description);
|
||||
assertEquals(0xFFFF0000, (int)info.color);
|
||||
assertEquals("tzdata", info.timeZone);
|
||||
assertTrue(info.supportsVEVENT);
|
||||
assertTrue(info.supportsVTODO);
|
||||
}
|
||||
|
||||
public void testFromDB() {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(Collections.ID, 1);
|
||||
values.put(Collections.SERVICE_ID, 1);
|
||||
values.put(Collections.URL, "http://example.com");
|
||||
values.put(Collections.READ_ONLY, 1);
|
||||
values.put(Collections.DISPLAY_NAME, "display name");
|
||||
values.put(Collections.DESCRIPTION, "description");
|
||||
values.put(Collections.COLOR, 0xFFFF0000);
|
||||
values.put(Collections.TIME_ZONE, "tzdata");
|
||||
values.put(Collections.SUPPORTS_VEVENT, 1);
|
||||
values.put(Collections.SUPPORTS_VTODO, 1);
|
||||
values.put(Collections.SYNC, 1);
|
||||
|
||||
CollectionInfo info = CollectionInfo.fromDB(values);
|
||||
assertEquals(1, info.id);
|
||||
assertEquals(1, (long)info.serviceID);
|
||||
assertEquals("http://example.com", info.url);
|
||||
assertTrue(info.readOnly);
|
||||
assertEquals("display name", info.displayName);
|
||||
assertEquals("description", info.description);
|
||||
assertEquals(0xFFFF0000, (int)info.color);
|
||||
assertEquals("tzdata", info.timeZone);
|
||||
assertTrue(info.supportsVEVENT);
|
||||
assertTrue(info.supportsVTODO);
|
||||
assertTrue(info.selected);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package at.bitfire.davdroid.resource;
|
||||
|
||||
import android.test.InstrumentationTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
import at.bitfire.dav4android.exception.DavException;
|
||||
import at.bitfire.dav4android.exception.HttpException;
|
||||
import at.bitfire.davdroid.ui.setup.DavResourceFinder;
|
||||
import at.bitfire.davdroid.ui.setup.LoginCredentials;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
|
||||
public class DavResourceFinderTest extends InstrumentationTestCase {
|
||||
|
||||
MockWebServer server = new MockWebServer();
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
server.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
server.shutdown();
|
||||
}
|
||||
|
||||
|
||||
public void testGetCurrentUserPrincipal() throws IOException, HttpException, DavException {
|
||||
HttpUrl url = server.url("/dav");
|
||||
LoginCredentials credentials = new LoginCredentials(url.uri(), "admin", "12345", true);
|
||||
DavResourceFinder finder = new DavResourceFinder(getInstrumentation().getTargetContext().getApplicationContext(), credentials);
|
||||
|
||||
// positive test case
|
||||
server.enqueue(new MockResponse() // PROPFIND response
|
||||
.setResponseCode(207)
|
||||
.setHeader("Content-Type", "application/xml;charset=utf-8")
|
||||
.setBody("<multistatus xmlns='DAV:'>" +
|
||||
" <response>" +
|
||||
" <href>/dav</href>" +
|
||||
" <propstat>" +
|
||||
" <prop>" +
|
||||
" <current-user-principal><href>/principals/myself</href></current-user-principal>" +
|
||||
" </prop>" +
|
||||
" <status>HTTP/1.0 200 OK</status>" +
|
||||
" </propstat>" +
|
||||
" </response>" +
|
||||
"</multistatus>"));
|
||||
server.enqueue(new MockResponse() // OPTIONS response
|
||||
.setResponseCode(200)
|
||||
.setHeader("DAV", "addressbook"));
|
||||
URI principal = finder.getCurrentUserPrincipal(url, DavResourceFinder.Service.CARDDAV);
|
||||
assertEquals(url.resolve("/principals/myself").uri(), principal);
|
||||
|
||||
// negative test case: no current-user-principal
|
||||
server.enqueue(new MockResponse()
|
||||
.setResponseCode(207)
|
||||
.setHeader("Content-Type", "application/xml;charset=utf-8")
|
||||
.setBody("<multistatus xmlns='DAV:'>" +
|
||||
" <response>" +
|
||||
" <href>/dav</href>" +
|
||||
" <status>HTTP/1.0 200 OK</status>" +
|
||||
" </response>" +
|
||||
"</multistatus>"));
|
||||
assertNull(finder.getCurrentUserPrincipal(url, DavResourceFinder.Service.CARDDAV));
|
||||
|
||||
// negative test case: requested service not available
|
||||
server.enqueue(new MockResponse() // PROPFIND response
|
||||
.setResponseCode(207)
|
||||
.setHeader("Content-Type", "application/xml;charset=utf-8")
|
||||
.setBody("<multistatus xmlns='DAV:'>" +
|
||||
" <response>" +
|
||||
" <href>/dav</href>" +
|
||||
" <propstat>" +
|
||||
" <prop>" +
|
||||
" <current-user-principal><href>/principals/myself</href></current-user-principal>" +
|
||||
" </prop>" +
|
||||
" <status>HTTP/1.0 200 OK</status>" +
|
||||
" </propstat>" +
|
||||
" </response>" +
|
||||
"</multistatus>"));
|
||||
server.enqueue(new MockResponse() // OPTIONS response
|
||||
.setResponseCode(200)
|
||||
.setHeader("DAV", "addressbook"));
|
||||
assertNull(finder.getCurrentUserPrincipal(url, DavResourceFinder.Service.CALDAV));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.ui.setup;
|
||||
|
||||
import android.test.InstrumentationTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
import at.bitfire.dav4android.DavResource;
|
||||
import at.bitfire.dav4android.exception.DavException;
|
||||
import at.bitfire.dav4android.exception.HttpException;
|
||||
import at.bitfire.dav4android.property.AddressbookHomeSet;
|
||||
import at.bitfire.dav4android.property.ResourceType;
|
||||
import at.bitfire.davdroid.App;
|
||||
import at.bitfire.davdroid.HttpClient;
|
||||
import at.bitfire.davdroid.ui.setup.DavResourceFinder;
|
||||
import at.bitfire.davdroid.ui.setup.DavResourceFinder.Configuration.ServiceInfo;
|
||||
import at.bitfire.davdroid.ui.setup.LoginCredentials;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.mockwebserver.Dispatcher;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import okhttp3.mockwebserver.RecordedRequest;
|
||||
|
||||
public class DavResourceFinderTest extends InstrumentationTestCase {
|
||||
|
||||
MockWebServer server = new MockWebServer();
|
||||
|
||||
DavResourceFinder finder;
|
||||
OkHttpClient client;
|
||||
LoginCredentials credentials;
|
||||
|
||||
private static final String
|
||||
PATH_NO_DAV = "/nodav",
|
||||
|
||||
PATH_CALDAV = "/caldav",
|
||||
PATH_CARDDAV = "/carddav",
|
||||
PATH_CALDAV_AND_CARDDAV = "/both-caldav-carddav",
|
||||
|
||||
SUBPATH_PRINCIPAL = "/principal",
|
||||
SUBPATH_ADDRESSBOOK_HOMESET = "/addressbooks",
|
||||
SUBPATH_ADDRESSBOOK = "/addressbooks/private-contacts";
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
server.setDispatcher(new TestDispatcher());
|
||||
server.start();
|
||||
|
||||
credentials = new LoginCredentials(URI.create("/"), "mock", "12345", true);
|
||||
finder = new DavResourceFinder(getInstrumentation().getContext(), credentials);
|
||||
|
||||
client = HttpClient.create();
|
||||
client = HttpClient.addAuthentication(client, credentials.userName, credentials.password, credentials.authPreemptive);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
server.shutdown();
|
||||
}
|
||||
|
||||
|
||||
public void testRememberIfAddressBookOrHomeset() throws IOException, HttpException, DavException {
|
||||
ServiceInfo info;
|
||||
|
||||
// before dav.propfind(), no info is available
|
||||
DavResource dav = new DavResource(client, server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL));
|
||||
finder.rememberIfAddressBookOrHomeset(dav, info = new ServiceInfo());
|
||||
assertEquals(0, info.collections.size());
|
||||
assertEquals(0, info.homeSets.size());
|
||||
|
||||
// recognize home set
|
||||
dav.propfind(0, AddressbookHomeSet.NAME);
|
||||
finder.rememberIfAddressBookOrHomeset(dav, info = new ServiceInfo());
|
||||
assertEquals(0, info.collections.size());
|
||||
assertEquals(1, info.homeSets.size());
|
||||
assertEquals(server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK_HOMESET + "/").uri(), info.homeSets.iterator().next());
|
||||
|
||||
// recognize address book
|
||||
dav = new DavResource(client, server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK));
|
||||
dav.propfind(0, ResourceType.NAME);
|
||||
finder.rememberIfAddressBookOrHomeset(dav, info = new ServiceInfo());
|
||||
assertEquals(1, info.collections.size());
|
||||
assertEquals(server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK + "/").uri(), info.collections.keySet().iterator().next());
|
||||
assertEquals(0, info.homeSets.size());
|
||||
}
|
||||
|
||||
public void testProvidesService() throws IOException {
|
||||
assertFalse(finder.providesService(server.url(PATH_NO_DAV), DavResourceFinder.Service.CALDAV));
|
||||
assertFalse(finder.providesService(server.url(PATH_NO_DAV), DavResourceFinder.Service.CARDDAV));
|
||||
|
||||
assertTrue(finder.providesService(server.url(PATH_CALDAV), DavResourceFinder.Service.CALDAV));
|
||||
assertFalse(finder.providesService(server.url(PATH_CALDAV), DavResourceFinder.Service.CARDDAV));
|
||||
|
||||
assertTrue(finder.providesService(server.url(PATH_CARDDAV), DavResourceFinder.Service.CARDDAV));
|
||||
assertFalse(finder.providesService(server.url(PATH_CARDDAV), DavResourceFinder.Service.CALDAV));
|
||||
|
||||
assertTrue(finder.providesService(server.url(PATH_CALDAV_AND_CARDDAV), DavResourceFinder.Service.CALDAV));
|
||||
assertTrue(finder.providesService(server.url(PATH_CALDAV_AND_CARDDAV), DavResourceFinder.Service.CARDDAV));
|
||||
}
|
||||
|
||||
public void testGetCurrentUserPrincipal() throws IOException, HttpException, DavException {
|
||||
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_NO_DAV), DavResourceFinder.Service.CALDAV));
|
||||
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_NO_DAV), DavResourceFinder.Service.CARDDAV));
|
||||
|
||||
assertEquals(
|
||||
server.url(PATH_CALDAV + SUBPATH_PRINCIPAL).uri(),
|
||||
finder.getCurrentUserPrincipal(server.url(PATH_CALDAV), DavResourceFinder.Service.CALDAV)
|
||||
);
|
||||
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_CALDAV), DavResourceFinder.Service.CARDDAV));
|
||||
|
||||
assertEquals(
|
||||
server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL).uri(),
|
||||
finder.getCurrentUserPrincipal(server.url(PATH_CARDDAV), DavResourceFinder.Service.CARDDAV)
|
||||
);
|
||||
assertNull(finder.getCurrentUserPrincipal(server.url(PATH_CARDDAV), DavResourceFinder.Service.CALDAV));
|
||||
}
|
||||
|
||||
|
||||
// mock server
|
||||
|
||||
public class TestDispatcher extends Dispatcher {
|
||||
|
||||
@Override
|
||||
public MockResponse dispatch(RecordedRequest rq) throws InterruptedException {
|
||||
if (!checkAuth(rq))
|
||||
return new MockResponse().setResponseCode(401);
|
||||
|
||||
String path = rq.getPath();
|
||||
|
||||
if ("OPTIONS".equalsIgnoreCase(rq.getMethod())) {
|
||||
String dav = null;
|
||||
if (path.startsWith(PATH_CALDAV))
|
||||
dav = "calendar-access";
|
||||
else if (path.startsWith(PATH_CARDDAV))
|
||||
dav = "addressbook";
|
||||
else if (path.startsWith(PATH_CALDAV_AND_CARDDAV))
|
||||
dav = "calendar-access, addressbook";
|
||||
MockResponse response = new MockResponse().setResponseCode(200);
|
||||
if (dav != null)
|
||||
response.addHeader("DAV", dav);
|
||||
return response;
|
||||
|
||||
} else if ("PROPFIND".equalsIgnoreCase(rq.getMethod())) {
|
||||
String props = null;
|
||||
switch (path) {
|
||||
case PATH_CALDAV:
|
||||
case PATH_CARDDAV:
|
||||
props = "<current-user-principal><href>" + path + SUBPATH_PRINCIPAL + "</href></current-user-principal>";
|
||||
break;
|
||||
|
||||
case PATH_CARDDAV + SUBPATH_PRINCIPAL:
|
||||
props = "<CARD:addressbook-home-set>" +
|
||||
" <href>" + PATH_CARDDAV + SUBPATH_ADDRESSBOOK_HOMESET + "</href>" +
|
||||
"</CARD:addressbook-home-set>";
|
||||
break;
|
||||
case PATH_CARDDAV + SUBPATH_ADDRESSBOOK:
|
||||
props = "<resourcetype>" +
|
||||
" <collection/>" +
|
||||
" <CARD:addressbook/>" +
|
||||
"</resourcetype>";
|
||||
break;
|
||||
}
|
||||
App.log.info("Sending props: " + props);
|
||||
return new MockResponse()
|
||||
.setResponseCode(207)
|
||||
.setBody("<multistatus xmlns='DAV:' xmlns:CARD='urn:ietf:params:xml:ns:carddav'>" +
|
||||
"<response>" +
|
||||
" <href>" + rq.getPath() + "</href>" +
|
||||
" <propstat><prop>" + props + "</prop></propstat>" +
|
||||
"</response>" +
|
||||
"</multistatus>");
|
||||
}
|
||||
|
||||
return new MockResponse().setResponseCode(404);
|
||||
}
|
||||
|
||||
private boolean checkAuth(RecordedRequest rq) {
|
||||
return "Basic bW9jazoxMjM0NQ==".equals(rq.getHeader("Authorization"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
55
app/src/gplay/res/menu/activity_accounts_drawer.xml
Normal file
55
app/src/gplay/res/menu/activity_accounts_drawer.xml
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
||||
~ All rights reserved. This program and the accompanying materials
|
||||
~ are made available under the terms of the GNU Public License v3.0
|
||||
~ which accompanies this distribution, and is available at
|
||||
~ http://www.gnu.org/licenses/gpl.html
|
||||
-->
|
||||
|
||||
<menu xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/nav_about"
|
||||
android:icon="@drawable/ic_info_dark"
|
||||
android:title="@string/navigation_drawer_about"/>
|
||||
<item
|
||||
android:id="@+id/nav_app_settings"
|
||||
android:icon="@drawable/ic_settings_dark"
|
||||
android:title="@string/navigation_drawer_settings"/>
|
||||
|
||||
<item android:title="@string/navigation_drawer_news_updates">
|
||||
<menu>
|
||||
<item
|
||||
android:id="@+id/nav_twitter"
|
||||
android:icon="@drawable/twitter"
|
||||
android:title="\@davdroidapp"
|
||||
tools:ignore="HardcodedText"/>
|
||||
</menu>
|
||||
</item>
|
||||
|
||||
<item android:title="@string/navigation_drawer_external_links">
|
||||
<menu>
|
||||
<item
|
||||
android:id="@+id/nav_website"
|
||||
android:icon="@drawable/ic_home_dark"
|
||||
android:title="@string/navigation_drawer_website"/>
|
||||
<item
|
||||
android:id="@+id/nav_faq"
|
||||
android:icon="@drawable/ic_help_dark"
|
||||
android:title="@string/navigation_drawer_faq"/>
|
||||
<item
|
||||
android:id="@+id/nav_forums"
|
||||
android:icon="@drawable/ic_forum_dark"
|
||||
android:title="@string/navigation_drawer_forums"/>
|
||||
<item
|
||||
android:id="@+id/nav_donate"
|
||||
android:icon="@drawable/ic_attach_money_dark"
|
||||
android:title="(entry disabled)"
|
||||
android:visible="false"
|
||||
tools:ignore="HardcodedText"/>
|
||||
</menu>
|
||||
</item>
|
||||
|
||||
</menu>
|
||||
@@ -12,6 +12,8 @@
|
||||
android:installLocation="internalOnly">
|
||||
|
||||
<!-- normal permissions -->
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
|
||||
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
|
||||
@@ -19,6 +21,10 @@
|
||||
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
|
||||
|
||||
<!-- legacy permissions -->
|
||||
<uses-permission
|
||||
android:name="android.permission.AUTHENTICATE_ACCOUNTS"
|
||||
android:maxSdkVersion="22"
|
||||
tools:ignore="UnusedAttribute"/>
|
||||
<!--
|
||||
for writing external log files; permission only required for SDK <= 18 because since then,
|
||||
writing to app-private directory doesn't require extra permissions
|
||||
@@ -33,12 +39,11 @@
|
||||
tools:ignore="UnusedAttribute"/>
|
||||
|
||||
<!-- other permissions -->
|
||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||
<!-- android.permission-group.CONTACTS -->
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
||||
<uses-permission android:name="android.permission.READ_CALENDAR"/>
|
||||
<!-- android.permission-group.CALENDAR -->
|
||||
<uses-permission android:name="android.permission.READ_CALENDAR"/>
|
||||
<uses-permission android:name="android.permission.WRITE_CALENDAR"/>
|
||||
|
||||
<!-- ical4android declares task access permissions -->
|
||||
@@ -60,6 +65,14 @@
|
||||
<action android:name="at.bitfire.davdroid.REINIT_LOGGER"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".AccountSettings$AppUpdatedReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
||||
<data android:scheme="package" android:path="at.bitfire.davdroid" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name=".syncadapter.AccountAuthenticatorService"
|
||||
@@ -127,7 +140,7 @@
|
||||
<action android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
|
||||
<activity
|
||||
android:name=".ui.AccountsActivity"
|
||||
android:label="@string/app_name"
|
||||
@@ -149,6 +162,10 @@
|
||||
android:label="@string/app_settings"
|
||||
android:parentActivityName=".ui.AccountsActivity"/>
|
||||
|
||||
<activity android:name=".ui.PermissionsActivity"
|
||||
android:label="@string/permissions_title"
|
||||
android:parentActivityName=".ui.AccountsActivity"/>
|
||||
|
||||
<activity
|
||||
android:name=".ui.setup.LoginActivity"
|
||||
android:label="@string/login_title"
|
||||
|
||||
@@ -9,30 +9,27 @@ package at.bitfire.davdroid;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.PeriodicSync;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.CalendarContract;
|
||||
import android.provider.CalendarContract.Calendars;
|
||||
import android.provider.ContactsContract;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.NotificationCompat;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -48,25 +45,46 @@ import at.bitfire.davdroid.resource.LocalTaskList;
|
||||
import at.bitfire.ical4android.CalendarStorageException;
|
||||
import at.bitfire.ical4android.TaskProvider;
|
||||
import at.bitfire.vcard4android.ContactsStorageException;
|
||||
import at.bitfire.vcard4android.GroupMethod;
|
||||
import lombok.Cleanup;
|
||||
import okhttp3.HttpUrl;
|
||||
|
||||
public class AccountSettings {
|
||||
private final static int CURRENT_VERSION = 3;
|
||||
private final static int CURRENT_VERSION = 4;
|
||||
private final static String
|
||||
KEY_SETTINGS_VERSION = "version",
|
||||
KEY_SETTINGS_VERSION = "version",
|
||||
|
||||
KEY_USERNAME = "user_name",
|
||||
KEY_AUTH_PREEMPTIVE = "auth_preemptive";
|
||||
KEY_USERNAME = "user_name",
|
||||
KEY_AUTH_PREEMPTIVE = "auth_preemptive",
|
||||
|
||||
KEY_WIFI_ONLY = "wifi_only", // sync on WiFi only (default: false)
|
||||
KEY_WIFI_ONLY_SSID = "wifi_only_ssid"; // restrict sync to specific WiFi SSID
|
||||
|
||||
/** Whether to use RFC 6868 for VCards
|
||||
* value = null (not existing) use RFC6868-style encoding (default value)
|
||||
* "0" don't use RFC 6868-style encoding
|
||||
*/
|
||||
private final static String KEY_VCARD_RFC6868 = "vcard_rfc6868";
|
||||
|
||||
/** Time range limitation to the past [in days]
|
||||
value = null default value (DEFAULT_TIME_RANGE_PAST_DAYS)
|
||||
< 0 (-1) no limit
|
||||
>= 0 entries more than n days in the past won't be synchronized
|
||||
value = null default value (DEFAULT_TIME_RANGE_PAST_DAYS)
|
||||
< 0 (-1) no limit
|
||||
>= 0 entries more than n days in the past won't be synchronized
|
||||
*/
|
||||
private final static String KEY_TIME_RANGE_PAST_DAYS = "time_range_past_days";
|
||||
private final static int DEFAULT_TIME_RANGE_PAST_DAYS = 90;
|
||||
|
||||
/* Whether DAVdroid sets the local calendar color to the value from service DB at every sync
|
||||
value = null (not existing) true (default)
|
||||
"0" false */
|
||||
private final static String KEY_MANAGE_CALENDAR_COLORS = "manage_calendar_colors";
|
||||
|
||||
/** Contact group method:
|
||||
value = null (not existing) groups as separate VCards (default)
|
||||
"CATEGORIES" groups are per-contact CATEGORIES
|
||||
*/
|
||||
private final static String KEY_CONTACT_GROUP_METHOD = "contact_group_method";
|
||||
|
||||
public final static long SYNC_INTERVAL_MANUALLY = -1;
|
||||
|
||||
final Context context;
|
||||
@@ -159,6 +177,38 @@ public class AccountSettings {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getSyncWifiOnly() {
|
||||
return accountManager.getUserData(account, KEY_WIFI_ONLY) != null;
|
||||
}
|
||||
|
||||
public void setSyncWiFiOnly(boolean wiFiOnly) {
|
||||
accountManager.setUserData(account, KEY_WIFI_ONLY, wiFiOnly ? "1" : null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getSyncWifiOnlySSID() {
|
||||
return accountManager.getUserData(account, KEY_WIFI_ONLY_SSID);
|
||||
}
|
||||
|
||||
public void setSyncWifiOnlySSID(String ssid) {
|
||||
accountManager.setUserData(account, KEY_WIFI_ONLY_SSID, ssid);
|
||||
}
|
||||
|
||||
|
||||
// CardDAV settings
|
||||
|
||||
public boolean getVCardRFC6868() {
|
||||
return accountManager.getUserData(account, KEY_VCARD_RFC6868) == null;
|
||||
}
|
||||
|
||||
public void setVCardRFC6868(boolean use) {
|
||||
accountManager.setUserData(account, KEY_VCARD_RFC6868, use ? null : "0");
|
||||
}
|
||||
|
||||
|
||||
// CalDAV settings
|
||||
|
||||
@Nullable
|
||||
public Integer getTimeRangePastDays() {
|
||||
String strDays = accountManager.getUserData(account, KEY_TIME_RANGE_PAST_DAYS);
|
||||
if (strDays != null) {
|
||||
@@ -168,10 +218,34 @@ public class AccountSettings {
|
||||
return DEFAULT_TIME_RANGE_PAST_DAYS;
|
||||
}
|
||||
|
||||
public void setTimeRangePastDays(Integer days) {
|
||||
public void setTimeRangePastDays(@Nullable Integer days) {
|
||||
accountManager.setUserData(account, KEY_TIME_RANGE_PAST_DAYS, String.valueOf(days == null ? -1 : days));
|
||||
}
|
||||
|
||||
public boolean getManageCalendarColors() {
|
||||
return accountManager.getUserData(account, KEY_MANAGE_CALENDAR_COLORS) == null;
|
||||
}
|
||||
|
||||
public void setManageCalendarColors(boolean manage) {
|
||||
accountManager.setUserData(account, KEY_MANAGE_CALENDAR_COLORS, manage ? null : "0");
|
||||
}
|
||||
|
||||
|
||||
// CardDAV settings
|
||||
|
||||
@NonNull
|
||||
public GroupMethod getGroupMethod() {
|
||||
final String name = accountManager.getUserData(account, KEY_CONTACT_GROUP_METHOD);
|
||||
return name != null ?
|
||||
GroupMethod.valueOf(name) :
|
||||
GroupMethod.GROUP_VCARDS;
|
||||
}
|
||||
|
||||
public void setGroupMethod(@NonNull GroupMethod method) {
|
||||
final String name = method == GroupMethod.GROUP_VCARDS ? null : method.name();
|
||||
accountManager.setUserData(account, KEY_CONTACT_GROUP_METHOD, name);
|
||||
}
|
||||
|
||||
|
||||
// update from previous account settings
|
||||
|
||||
@@ -181,6 +255,7 @@ public class AccountSettings {
|
||||
try {
|
||||
Method updateProc = getClass().getDeclaredMethod("update_" + fromVersion + "_" + toVersion);
|
||||
updateProc.invoke(this);
|
||||
accountManager.setUserData(account, KEY_SETTINGS_VERSION, String.valueOf(toVersion));
|
||||
} catch (Exception e) {
|
||||
App.log.log(Level.SEVERE, "Couldn't update account settings", e);
|
||||
}
|
||||
@@ -217,8 +292,6 @@ public class AccountSettings {
|
||||
if (!TextUtils.isEmpty(cTag))
|
||||
addr.setCTag(cTag);
|
||||
accountManager.setUserData(account, "addressbook_ctag", null);
|
||||
|
||||
accountManager.setUserData(account, KEY_SETTINGS_VERSION, "2");
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "Recycle", "unused" })
|
||||
@@ -344,8 +417,32 @@ public class AccountSettings {
|
||||
refresh.putExtra(DavService.EXTRA_DAV_SERVICE_ID, serviceCalDAV);
|
||||
context.startService(refresh);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "Recycle", "unused" })
|
||||
private void update_3_4() {
|
||||
setGroupMethod(GroupMethod.CATEGORIES);
|
||||
}
|
||||
|
||||
|
||||
public static class AppUpdatedReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
@SuppressLint("UnsafeProtectedBroadcastReceiver")
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
App.log.info("DAVdroid was updated, checking for AccountSettings version");
|
||||
|
||||
// peek into AccountSettings to initiate a possible migration
|
||||
AccountManager accountManager = AccountManager.get(context);
|
||||
for (Account account : accountManager.getAccountsByType(Constants.ACCOUNT_TYPE))
|
||||
try {
|
||||
App.log.info("Checking account " + account.name);
|
||||
new AccountSettings(context, account);
|
||||
} catch (InvalidAccountException e) {
|
||||
App.log.log(Level.SEVERE, "Couldn't check for updated account settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
accountManager.setUserData(account, KEY_SETTINGS_VERSION, "3");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ import lombok.Getter;
|
||||
import okhttp3.internal.tls.OkHostnameVerifier;
|
||||
|
||||
public class App extends Application {
|
||||
public static final String FLAVOR_GOOGLE_PLAY = "gplay";
|
||||
|
||||
public static final String LOG_TO_EXTERNAL_STORAGE = "logToExternalStorage";
|
||||
|
||||
@Getter
|
||||
@@ -50,6 +52,9 @@ public class App extends Application {
|
||||
private static HostnameVerifier hostnameVerifier;
|
||||
|
||||
public final static Logger log = Logger.getLogger("davdroid");
|
||||
static {
|
||||
at.bitfire.dav4android.Constants.log = Logger.getLogger("davdroid.dav4android");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
|
||||
@@ -21,7 +21,8 @@ public class Constants {
|
||||
NOTIFICATION_REFRESH_COLLECTIONS = 2,
|
||||
NOTIFICATION_CONTACTS_SYNC = 10,
|
||||
NOTIFICATION_CALENDAR_SYNC = 11,
|
||||
NOTIFICATION_TASK_SYNC = 12;
|
||||
NOTIFICATION_TASK_SYNC = 12,
|
||||
NOTIFICATION_PERMISSIONS = 20;
|
||||
|
||||
public static final Uri webUri = Uri.parse("https://davdroid.bitfire.at/?pk_campaign=davdroid-app");
|
||||
|
||||
|
||||
@@ -22,10 +22,16 @@ import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.NotificationCompat;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.apache.commons.collections4.iterators.IteratorChain;
|
||||
import org.apache.commons.collections4.iterators.SingletonIterator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -65,25 +71,30 @@ public class DavService extends Service {
|
||||
private final IBinder binder = new InfoBinder();
|
||||
|
||||
private final Set<Long> runningRefresh = new HashSet<>();
|
||||
private final List<RefreshingStatusListener> refreshingStatusListeners = new LinkedList<>();
|
||||
private final List<WeakReference<RefreshingStatusListener>> refreshingStatusListeners = new LinkedList<>();
|
||||
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
String action = intent.getAction();
|
||||
long id = intent.getLongExtra(EXTRA_DAV_SERVICE_ID, -1);
|
||||
if (intent != null) {
|
||||
String action = intent.getAction();
|
||||
long id = intent.getLongExtra(EXTRA_DAV_SERVICE_ID, -1);
|
||||
|
||||
switch (action) {
|
||||
case ACTION_ACCOUNTS_UPDATED:
|
||||
cleanupAccounts();
|
||||
break;
|
||||
case ACTION_REFRESH_COLLECTIONS:
|
||||
if (runningRefresh.add(id)) {
|
||||
new Thread(new RefreshCollections(id)).start();
|
||||
for (RefreshingStatusListener listener : refreshingStatusListeners)
|
||||
listener.onDavRefreshStatusChanged(id, true);
|
||||
}
|
||||
break;
|
||||
switch (action) {
|
||||
case ACTION_ACCOUNTS_UPDATED:
|
||||
cleanupAccounts();
|
||||
break;
|
||||
case ACTION_REFRESH_COLLECTIONS:
|
||||
if (runningRefresh.add(id)) {
|
||||
new Thread(new RefreshCollections(id)).start();
|
||||
for (WeakReference<RefreshingStatusListener> ref : refreshingStatusListeners) {
|
||||
RefreshingStatusListener listener = ref.get();
|
||||
if (listener != null)
|
||||
listener.onDavRefreshStatusChanged(id, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return START_NOT_STICKY;
|
||||
@@ -108,15 +119,19 @@ public class DavService extends Service {
|
||||
return runningRefresh.contains(id);
|
||||
}
|
||||
|
||||
public void addRefreshingStatusListener(RefreshingStatusListener listener, boolean callImmediate) {
|
||||
refreshingStatusListeners.add(listener);
|
||||
public void addRefreshingStatusListener(@NonNull RefreshingStatusListener listener, boolean callImmediate) {
|
||||
refreshingStatusListeners.add(new WeakReference<>(listener));
|
||||
if (callImmediate)
|
||||
for (long id : runningRefresh)
|
||||
listener.onDavRefreshStatusChanged(id, true);
|
||||
}
|
||||
|
||||
public void removeRefreshingStatusListener(RefreshingStatusListener listener) {
|
||||
refreshingStatusListeners.remove(listener);
|
||||
public void removeRefreshingStatusListener(@NonNull RefreshingStatusListener listener) {
|
||||
for (Iterator<WeakReference<RefreshingStatusListener>> iterator = refreshingStatusListeners.iterator(); iterator.hasNext(); ) {
|
||||
RefreshingStatusListener item = iterator.next().get();
|
||||
if (listener.equals(item))
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,14 +232,16 @@ public class DavService extends Service {
|
||||
if (info.selected)
|
||||
selectedCollections.add(HttpUrl.parse(info.url));
|
||||
|
||||
for (Iterator<HttpUrl> iterator = homeSets.iterator(); iterator.hasNext(); ) {
|
||||
HttpUrl homeSet = iterator.next();
|
||||
for (Iterator<HttpUrl> itHomeSets = homeSets.iterator(); itHomeSets.hasNext(); ) {
|
||||
HttpUrl homeSet = itHomeSets.next();
|
||||
App.log.fine("Listing home set " + homeSet);
|
||||
|
||||
DavResource dav = new DavResource(httpClient, homeSet);
|
||||
try {
|
||||
dav.propfind(1, CollectionInfo.DAV_PROPERTIES);
|
||||
for (DavResource member : dav.members) {
|
||||
IteratorChain<DavResource> itCollections = new IteratorChain<>(dav.members.iterator(), new SingletonIterator(dav));
|
||||
while (itCollections.hasNext()) {
|
||||
DavResource member = itCollections.next();
|
||||
CollectionInfo info = CollectionInfo.fromDavResource(member);
|
||||
info.confirmed = true;
|
||||
App.log.log(Level.FINE, "Found collection", info);
|
||||
@@ -236,7 +253,7 @@ public class DavService extends Service {
|
||||
} catch(HttpException e) {
|
||||
if (e.status == 403 || e.status == 404 || e.status == 410)
|
||||
// delete home set only if it was not accessible (40x)
|
||||
iterator.remove();
|
||||
itHomeSets.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,7 +291,7 @@ public class DavService extends Service {
|
||||
}
|
||||
|
||||
try {
|
||||
db.beginTransaction();
|
||||
db.beginTransactionNonExclusive();
|
||||
saveHomeSets(homeSets);
|
||||
saveCollections(collections.values());
|
||||
db.setTransactionSuccessful();
|
||||
@@ -288,7 +305,7 @@ public class DavService extends Service {
|
||||
App.log.log(Level.SEVERE, "Couldn't refresh collection list", e);
|
||||
|
||||
Intent debugIntent = new Intent(DavService.this, DebugInfoActivity.class);
|
||||
debugIntent.putExtra(DebugInfoActivity.KEY_EXCEPTION, e);
|
||||
debugIntent.putExtra(DebugInfoActivity.KEY_THROWABLE, e);
|
||||
if (account != null)
|
||||
debugIntent.putExtra(DebugInfoActivity.KEY_ACCOUNT, account);
|
||||
|
||||
@@ -298,15 +315,18 @@ public class DavService extends Service {
|
||||
.setLargeIcon(((BitmapDrawable)getResources().getDrawable(R.drawable.ic_launcher)).getBitmap())
|
||||
.setContentTitle(getString(R.string.dav_service_refresh_failed))
|
||||
.setContentText(getString(R.string.dav_service_refresh_couldnt_refresh))
|
||||
.setContentIntent(PendingIntent.getActivity(DavService.this, 0, debugIntent, 0))
|
||||
.setContentIntent(PendingIntent.getActivity(DavService.this, 0, debugIntent, PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
.build();
|
||||
nm.notify(Constants.NOTIFICATION_REFRESH_COLLECTIONS, notify);
|
||||
} finally {
|
||||
dbHelper.close();
|
||||
|
||||
runningRefresh.remove(service);
|
||||
for (RefreshingStatusListener listener : refreshingStatusListeners)
|
||||
listener.onDavRefreshStatusChanged(service, false);
|
||||
for (WeakReference<RefreshingStatusListener> ref : refreshingStatusListeners) {
|
||||
RefreshingStatusListener listener = ref.get();
|
||||
if (listener != null)
|
||||
listener.onDavRefreshStatusChanged(service, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,6 +353,7 @@ public class DavService extends Service {
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
private Account account() {
|
||||
@Cleanup Cursor cursor = db.query(Services._TABLE, new String[] { Services.ACCOUNT_NAME }, Services.ID + "=?", new String[] { String.valueOf(service) }, null, null, null);
|
||||
if (cursor.moveToNext()) {
|
||||
@@ -341,6 +362,7 @@ public class DavService extends Service {
|
||||
throw new IllegalArgumentException("Service not found");
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private String serviceType() {
|
||||
@Cleanup Cursor cursor = db.query(Services._TABLE, new String[] { Services.SERVICE }, Services.ID + "=?", new String[] { String.valueOf(service) }, null, null, null);
|
||||
if (cursor.moveToNext())
|
||||
@@ -349,6 +371,7 @@ public class DavService extends Service {
|
||||
throw new IllegalArgumentException("Service not found");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private HttpUrl readPrincipal() {
|
||||
@Cleanup Cursor cursor = db.query(Services._TABLE, new String[] { Services.PRINCIPAL }, Services.ID + "=?", new String[] { String.valueOf(service) }, null, null, null);
|
||||
if (cursor.moveToNext()) {
|
||||
@@ -359,6 +382,7 @@ public class DavService extends Service {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Set<HttpUrl> readHomeSets() {
|
||||
Set<HttpUrl> homeSets = new LinkedHashSet<>();
|
||||
@Cleanup Cursor cursor = db.query(HomeSets._TABLE, new String[] { HomeSets.URL }, HomeSets.SERVICE_ID + "=?", new String[] { String.valueOf(service) }, null, null, null);
|
||||
@@ -377,6 +401,7 @@ public class DavService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Map<HttpUrl, CollectionInfo> readCollections() {
|
||||
Map<HttpUrl, CollectionInfo> collections = new LinkedHashMap<>();
|
||||
@Cleanup Cursor cursor = db.query(Collections._TABLE, null, Collections.SERVICE_ID + "=?", new String[]{String.valueOf(service)}, null, null, null);
|
||||
|
||||
@@ -8,8 +8,15 @@
|
||||
|
||||
package at.bitfire.davdroid;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
|
||||
public class DavUtils {
|
||||
|
||||
@@ -19,4 +26,16 @@ public class DavUtils {
|
||||
return String.format("#%06X%02X", color, alpha);
|
||||
}
|
||||
|
||||
public static String lastSegmentOfUrl(@NonNull String url) {
|
||||
// the list returned by HttpUrl.pathSegments() is unmodifiable, so we have to create a copy
|
||||
List<String> segments = new LinkedList<>(HttpUrl.parse(url).pathSegments());
|
||||
Collections.reverse(segments);
|
||||
|
||||
for (String segment : segments)
|
||||
if (!StringUtils.isEmpty(segment))
|
||||
return segment;
|
||||
|
||||
return "/";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public class HttpClient {
|
||||
|
||||
// use MemorizingTrustManager to manage self-signed certificates
|
||||
if (App.getSslSocketFactoryCompat() != null)
|
||||
builder.sslSocketFactory(App.getSslSocketFactoryCompat());
|
||||
builder.sslSocketFactory(App.getSslSocketFactoryCompat(), App.getMemorizingTrustManager());
|
||||
if (App.getHostnameVerifier() != null)
|
||||
builder.hostnameVerifier(App.getHostnameVerifier());
|
||||
|
||||
@@ -90,7 +90,7 @@ public class HttpClient {
|
||||
builder.addNetworkInterceptor(userAgentInterceptor);
|
||||
|
||||
// add cookie store for non-persistent cookies (some services like Horde use cookies for session tracking)
|
||||
builder.cookieJar(MemoryCookieStore.INSTANCE);
|
||||
builder.cookieJar(new MemoryCookieStore());
|
||||
|
||||
// add network logging, if requested
|
||||
if (logger.isLoggable(Level.FINEST)) {
|
||||
|
||||
@@ -8,10 +8,13 @@
|
||||
|
||||
package at.bitfire.davdroid;
|
||||
|
||||
import java.util.Collections;
|
||||
import org.apache.commons.collections4.MapIterator;
|
||||
import org.apache.commons.collections4.keyvalue.MultiKey;
|
||||
import org.apache.commons.collections4.map.HashedMap;
|
||||
import org.apache.commons.collections4.map.MultiKeyMap;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import okhttp3.Cookie;
|
||||
import okhttp3.CookieJar;
|
||||
@@ -23,22 +26,42 @@ import okhttp3.HttpUrl;
|
||||
*/
|
||||
public class MemoryCookieStore implements CookieJar {
|
||||
|
||||
public static final MemoryCookieStore INSTANCE = new MemoryCookieStore();
|
||||
|
||||
protected final Map<HttpUrl, List<Cookie>> store = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Stored cookies. The multi-key consists of three parts: name, domain, and path.
|
||||
* This ensures that cookies can be overwritten. [RFC 6265 5.3 Storage Model]
|
||||
* Not thread-safe!
|
||||
*/
|
||||
protected final MultiKeyMap<String, Cookie> storage = MultiKeyMap.multiKeyMap(new HashedMap<MultiKey<? extends String>, Cookie>());
|
||||
|
||||
@Override
|
||||
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
|
||||
store.put(url, cookies);
|
||||
synchronized(storage) {
|
||||
for (Cookie cookie : cookies)
|
||||
storage.put(cookie.name(), cookie.domain(), cookie.path(), cookie);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Cookie> loadForRequest(HttpUrl url) {
|
||||
List<Cookie> cookies = store.get(url);
|
||||
List<Cookie> cookies = new LinkedList<>();
|
||||
|
||||
if (cookies == null)
|
||||
cookies = Collections.emptyList();
|
||||
synchronized(storage) {
|
||||
MapIterator<MultiKey<? extends String>, Cookie> iter = storage.mapIterator();
|
||||
while (iter.hasNext()) {
|
||||
iter.next();
|
||||
Cookie cookie = iter.getValue();
|
||||
|
||||
// remove expired cookies
|
||||
if (cookie.expiresAt() <= System.currentTimeMillis()) {
|
||||
iter.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
// add applicable cookies
|
||||
if (cookie.matches(url))
|
||||
cookies.add(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
return cookies;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import android.text.TextUtils;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
@@ -75,8 +74,8 @@ public class SSLSocketFactoryCompat extends SSLSocketFactory {
|
||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
|
||||
List<String> availableCiphers = Arrays.asList(socket.getSupportedCipherSuites());
|
||||
App.log.fine("Available cipher suites: " + TextUtils.join(", ", availableCiphers));
|
||||
App.log.fine("Cipher suites enabled by default: " + TextUtils.join(", ", socket.getEnabledCipherSuites()));
|
||||
App.log.info("Available cipher suites: " + TextUtils.join(", ", availableCiphers));
|
||||
App.log.info("Cipher suites enabled by default: " + TextUtils.join(", ", socket.getEnabledCipherSuites()));
|
||||
|
||||
// take all allowed ciphers that are available and put them into preferredCiphers
|
||||
HashSet<String> preferredCiphers = new HashSet<>(allowedCiphers);
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
|
||||
package at.bitfire.davdroid.log;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
@@ -33,10 +31,9 @@ public class PlainTextFormatter extends Formatter {
|
||||
public String format(LogRecord r) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
if (!logcat) {
|
||||
builder.append(DateFormatUtils.format(r.getMillis(), "yyyy-MM-dd HH:mm:ss"));
|
||||
builder.append(String.format(" %d ", r.getThreadID()));
|
||||
}
|
||||
if (!logcat)
|
||||
builder .append(DateFormatUtils.format(r.getMillis(), "yyyy-MM-dd HH:mm:ss"))
|
||||
.append(" ").append(r.getThreadID()).append(" ");
|
||||
|
||||
builder.append(String.format("[%s] %s", shortClassName(r.getSourceClassName()), r.getMessage()));
|
||||
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.model;
|
||||
|
||||
import android.content.ContentValues;
|
||||
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
public class HomeSet {
|
||||
|
||||
public long id, serviceID;
|
||||
public String URL;
|
||||
|
||||
public static HomeSet fromDB(ContentValues values) {
|
||||
HomeSet homeSet = new HomeSet();
|
||||
homeSet.id = values.getAsLong(ServiceDB.HomeSets.ID);
|
||||
homeSet.serviceID = values.getAsLong(ServiceDB.HomeSets.SERVICE_ID);
|
||||
homeSet.URL = values.getAsString(ServiceDB.HomeSets.URL);
|
||||
return homeSet;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.model;
|
||||
|
||||
import android.content.ContentValues;
|
||||
|
||||
public class Service {
|
||||
|
||||
public long id;
|
||||
public String accountName, service, principal;
|
||||
public long lastRefresh;
|
||||
|
||||
|
||||
public static Service fromDB(ContentValues values) {
|
||||
Service service = new Service();
|
||||
service.id = values.getAsLong(ServiceDB.Services.ID);
|
||||
service.accountName = values.getAsString(ServiceDB.Services.ACCOUNT_NAME);
|
||||
service.service = values.getAsString(ServiceDB.Services.SERVICE);
|
||||
service.principal = values.getAsString(ServiceDB.Services.PRINCIPAL);
|
||||
return service;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -72,20 +72,26 @@ public class ServiceDB {
|
||||
|
||||
public OpenHelper(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
|
||||
setWriteAheadLoggingEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(SQLiteDatabase db) {
|
||||
db.enableWriteAheadLogging();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
|
||||
db.setForeignKeyConstraintsEnabled(true);
|
||||
else
|
||||
else {
|
||||
if (!db.enableWriteAheadLogging())
|
||||
App.log.warning("Couldn't enable write-ahead logging");
|
||||
|
||||
db.execSQL("PRAGMA foreign_keys=ON;");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
App.log.info("Creating services database");
|
||||
App.log.info("Creating database " + db.getPath());
|
||||
|
||||
db.execSQL("CREATE TABLE " + Settings._TABLE + "(" +
|
||||
Settings.NAME + " TEXT NOT NULL," +
|
||||
@@ -116,7 +122,7 @@ public class ServiceDB {
|
||||
Collections.DISPLAY_NAME + " TEXT NULL," +
|
||||
Collections.DESCRIPTION + " TEXT NULL," +
|
||||
Collections.COLOR + " INTEGER NULL," +
|
||||
Collections.TIME_ZONE + " TEXt NULL," +
|
||||
Collections.TIME_ZONE + " TEXT NULL," +
|
||||
Collections.SUPPORTS_VEVENT + " INTEGER NULL," +
|
||||
Collections.SUPPORTS_VTODO + " INTEGER NULL," +
|
||||
Collections.SYNC + " INTEGER DEFAULT 0 NOT NULL" +
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.model;
|
||||
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
|
||||
public class UnknownProperties {
|
||||
|
||||
public static final String CONTENT_ITEM_TYPE = "x.davdroid/unknown-properties";
|
||||
|
||||
public static final String
|
||||
MIMETYPE = RawContacts.Data.MIMETYPE,
|
||||
RAW_CONTACT_ID = RawContacts.Data.RAW_CONTACT_ID,
|
||||
UNKNOWN_PROPERTIES = RawContacts.Data.DATA1;
|
||||
|
||||
}
|
||||
@@ -12,22 +12,22 @@ import android.content.ContentProviderClient;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
|
||||
import android.provider.ContactsContract.Data;
|
||||
import android.provider.ContactsContract.Groups;
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import at.bitfire.davdroid.App;
|
||||
import at.bitfire.vcard4android.AndroidAddressBook;
|
||||
import at.bitfire.vcard4android.AndroidContact;
|
||||
import at.bitfire.vcard4android.AndroidGroupFactory;
|
||||
import at.bitfire.vcard4android.AndroidGroup;
|
||||
import at.bitfire.vcard4android.ContactsStorageException;
|
||||
import lombok.Cleanup;
|
||||
|
||||
@@ -40,121 +40,135 @@ public class LocalAddressBook extends AndroidAddressBook implements LocalCollect
|
||||
|
||||
private final Bundle syncState = new Bundle();
|
||||
|
||||
/**
|
||||
* Whether contact groups (LocalGroup resources) are included in query results for
|
||||
* {@link #getAll()}, {@link #getDeleted()}, {@link #getDirty()} and
|
||||
* {@link #getWithoutFileName()}.
|
||||
*/
|
||||
public boolean includeGroups = true;
|
||||
|
||||
|
||||
public LocalAddressBook(Account account, ContentProviderClient provider) {
|
||||
super(account, provider, AndroidGroupFactory.INSTANCE, LocalContact.Factory.INSTANCE);
|
||||
super(account, provider, LocalGroup.Factory.INSTANCE, LocalContact.Factory.INSTANCE);
|
||||
}
|
||||
|
||||
public LocalContact findContactByUID(String uid) throws ContactsStorageException, FileNotFoundException {
|
||||
LocalContact[] contacts = (LocalContact[])queryContacts(LocalContact.COLUMN_UID + "=?", new String[] { uid });
|
||||
if (contacts.length == 0)
|
||||
throw new FileNotFoundException();
|
||||
return contacts[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of local contacts, excluding those which have been modified locally (and not uploaded yet).
|
||||
*/
|
||||
@Override
|
||||
public LocalContact[] getAll() throws ContactsStorageException {
|
||||
return (LocalContact[])queryContacts(null, null);
|
||||
public LocalResource[] getAll() throws ContactsStorageException {
|
||||
List<LocalResource> all = new LinkedList<>();
|
||||
Collections.addAll(all, (LocalResource[])queryContacts(null, null));
|
||||
if (includeGroups)
|
||||
Collections.addAll(all, (LocalResource[])queryGroups(null, null));
|
||||
return all.toArray(new LocalResource[all.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of local contacts which have been deleted locally. (DELETED != 0).
|
||||
* Returns an array of local contacts/groups which have been deleted locally. (DELETED != 0).
|
||||
*/
|
||||
@Override
|
||||
public LocalContact[] getDeleted() throws ContactsStorageException {
|
||||
return (LocalContact[])queryContacts(RawContacts.DELETED + "!=0", null);
|
||||
public LocalResource[] getDeleted() throws ContactsStorageException {
|
||||
List<LocalResource> deleted = new LinkedList<>();
|
||||
Collections.addAll(deleted, getDeletedContacts());
|
||||
if (includeGroups)
|
||||
Collections.addAll(deleted, getDeletedGroups());
|
||||
return deleted.toArray(new LocalResource[deleted.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of local contacts which have been changed locally (DIRTY != 0).
|
||||
* Returns an array of local contacts/groups which have been changed locally (DIRTY != 0).
|
||||
*/
|
||||
@Override
|
||||
public LocalContact[] getDirty() throws ContactsStorageException {
|
||||
return (LocalContact[])queryContacts(RawContacts.DIRTY + "!=0", null);
|
||||
public LocalResource[] getDirty() throws ContactsStorageException {
|
||||
List<LocalResource> dirty = new LinkedList<>();
|
||||
Collections.addAll(dirty, getDirtyContacts());
|
||||
if (includeGroups)
|
||||
Collections.addAll(dirty, getDirtyGroups());
|
||||
return dirty.toArray(new LocalResource[dirty.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of local contacts which don't have a file name yet.
|
||||
*/
|
||||
@Override
|
||||
public LocalContact[] getWithoutFileName() throws ContactsStorageException {
|
||||
return (LocalContact[])queryContacts(AndroidContact.COLUMN_FILENAME + " IS NULL", null);
|
||||
public LocalResource[] getWithoutFileName() throws ContactsStorageException {
|
||||
List<LocalResource> nameless = new LinkedList<>();
|
||||
Collections.addAll(nameless, (LocalContact[])queryContacts(AndroidContact.COLUMN_FILENAME + " IS NULL", null));
|
||||
if (includeGroups)
|
||||
Collections.addAll(nameless, (LocalGroup[])queryGroups(AndroidGroup.COLUMN_FILENAME + " IS NULL", null));
|
||||
return nameless.toArray(new LocalResource[nameless.size()]);
|
||||
}
|
||||
|
||||
public void deleteAll() throws ContactsStorageException {
|
||||
public void deleteAll() throws ContactsStorageException {
|
||||
try {
|
||||
provider.delete(syncAdapterURI(RawContacts.CONTENT_URI), null, null);
|
||||
} catch (RemoteException e) {
|
||||
throw new ContactsStorageException("Couldn't delete all local contacts", e);
|
||||
provider.delete(syncAdapterURI(Groups.CONTENT_URI), null, null);
|
||||
} catch(RemoteException e) {
|
||||
throw new ContactsStorageException("Couldn't delete all local contacts and groups", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// GROUPS
|
||||
public LocalContact[] getDeletedContacts() throws ContactsStorageException {
|
||||
return (LocalContact[])queryContacts(RawContacts.DELETED + "!= 0", null);
|
||||
}
|
||||
|
||||
public LocalContact[] getDirtyContacts() throws ContactsStorageException {
|
||||
return (LocalContact[])queryContacts(RawContacts.DIRTY + "!= 0", null);
|
||||
}
|
||||
|
||||
public LocalGroup[] getDeletedGroups() throws ContactsStorageException {
|
||||
return (LocalGroup[])queryGroups(Groups.DELETED + "!= 0", null);
|
||||
}
|
||||
|
||||
public LocalGroup[] getDirtyGroups() throws ContactsStorageException {
|
||||
return (LocalGroup[])queryGroups(Groups.DIRTY + "!= 0", null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds the first group with the given title.
|
||||
* @param displayName title of the group to look for
|
||||
* @return group with given title, or null if none
|
||||
* Finds the first group with the given title. If there is no group with this
|
||||
* title, a new group is created.
|
||||
* @param title title of the group to look for
|
||||
* @return id of the group with given title
|
||||
* @throws ContactsStorageException on contact provider errors
|
||||
*/
|
||||
@SuppressWarnings("Recycle")
|
||||
public LocalGroup findGroupByTitle(String displayName) throws ContactsStorageException {
|
||||
public long findOrCreateGroup(@NonNull String title) throws ContactsStorageException {
|
||||
try {
|
||||
@Cleanup Cursor cursor = provider.query(syncAdapterURI(Groups.CONTENT_URI),
|
||||
new String[] { Groups._ID },
|
||||
ContactsContract.Groups.TITLE + "=?", new String[] { displayName }, null);
|
||||
new String[] { Groups._ID },
|
||||
Groups.TITLE + "=?", new String[] { title },
|
||||
null);
|
||||
if (cursor != null && cursor.moveToNext())
|
||||
return new LocalGroup(this, cursor.getLong(0));
|
||||
} catch (RemoteException e) {
|
||||
return cursor.getLong(0);
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(Groups.TITLE, title);
|
||||
Uri uri = provider.insert(syncAdapterURI(Groups.CONTENT_URI), values);
|
||||
return ContentUris.parseId(uri);
|
||||
} catch(RemoteException e) {
|
||||
throw new ContactsStorageException("Couldn't find local contact group", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("Recycle")
|
||||
public LocalGroup[] getDeletedGroups() throws ContactsStorageException {
|
||||
List<LocalGroup> groups = new LinkedList<>();
|
||||
try {
|
||||
@Cleanup Cursor cursor = provider.query(syncAdapterURI(Groups.CONTENT_URI),
|
||||
new String[] { Groups._ID },
|
||||
Groups.DELETED + "!=0", null, null);
|
||||
while (cursor != null && cursor.moveToNext())
|
||||
groups.add(new LocalGroup(this, cursor.getLong(0)));
|
||||
} catch (RemoteException e) {
|
||||
throw new ContactsStorageException("Couldn't query deleted groups", e);
|
||||
}
|
||||
return groups.toArray(new LocalGroup[groups.size()]);
|
||||
public void removeEmptyGroups() throws ContactsStorageException {
|
||||
// find groups without members
|
||||
/** should be done using {@link Groups.SUMMARY_COUNT}, but it's not implemented in Android yet */
|
||||
for (LocalGroup group : (LocalGroup[])queryGroups(null, null))
|
||||
if (group.getMembers().length == 0)
|
||||
group.delete();
|
||||
}
|
||||
|
||||
@SuppressWarnings("Recycle")
|
||||
public LocalGroup[] getDirtyGroups() throws ContactsStorageException {
|
||||
List<LocalGroup> groups = new LinkedList<>();
|
||||
public void removeGroups() throws ContactsStorageException {
|
||||
try {
|
||||
@Cleanup Cursor cursor = provider.query(syncAdapterURI(Groups.CONTENT_URI),
|
||||
new String[] { Groups._ID },
|
||||
Groups.DIRTY + "!=0", null, null);
|
||||
while (cursor != null && cursor.moveToNext())
|
||||
groups.add(new LocalGroup(this, cursor.getLong(0)));
|
||||
} catch (RemoteException e) {
|
||||
throw new ContactsStorageException("Couldn't query dirty groups", e);
|
||||
}
|
||||
return groups.toArray(new LocalGroup[groups.size()]);
|
||||
}
|
||||
|
||||
@SuppressWarnings("Recycle")
|
||||
public void markMembersDirty(long groupId) throws ContactsStorageException {
|
||||
ContentValues dirty = new ContentValues(1);
|
||||
dirty.put(RawContacts.DIRTY, 1);
|
||||
try {
|
||||
// query all GroupMemberships of this groupId, mark every corresponding raw contact as DIRTY
|
||||
@Cleanup Cursor cursor = provider.query(syncAdapterURI(Data.CONTENT_URI),
|
||||
new String[] { GroupMembership.RAW_CONTACT_ID },
|
||||
Data.MIMETYPE + "=? AND " + GroupMembership.GROUP_ROW_ID + "=?",
|
||||
new String[] { GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(groupId) }, null);
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
long id = cursor.getLong(0);
|
||||
App.log.fine("Marking raw contact #" + id + " as dirty");
|
||||
provider.update(syncAdapterURI(ContentUris.withAppendedId(RawContacts.CONTENT_URI, id)), dirty, null, null);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw new ContactsStorageException("Couldn't query dirty groups", e);
|
||||
provider.delete(syncAdapterURI(Groups.CONTENT_URI), null, null);
|
||||
} catch(RemoteException e) {
|
||||
throw new ContactsStorageException("Couldn't remove all groups", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import at.bitfire.davdroid.App;
|
||||
import at.bitfire.davdroid.DavUtils;
|
||||
import at.bitfire.davdroid.model.CollectionInfo;
|
||||
import at.bitfire.ical4android.AndroidCalendar;
|
||||
import at.bitfire.ical4android.AndroidCalendarFactory;
|
||||
@@ -71,26 +72,32 @@ public class LocalCalendar extends AndroidCalendar implements LocalCollection {
|
||||
}
|
||||
|
||||
public static Uri create(@NonNull Account account, @NonNull ContentProviderClient provider, @NonNull CollectionInfo info) throws CalendarStorageException {
|
||||
ContentValues values = valuesFromCollectionInfo(info);
|
||||
ContentValues values = valuesFromCollectionInfo(info, true);
|
||||
|
||||
// ACCOUNT_NAME and ACCOUNT_TYPE are required (see docs)! If it's missing, other apps will crash.
|
||||
values.put(Calendars.ACCOUNT_NAME, account.name);
|
||||
values.put(Calendars.ACCOUNT_TYPE, account.type);
|
||||
values.put(Calendars.OWNER_ACCOUNT, account.name);
|
||||
|
||||
// flag as visible & synchronizable at creation, might be changed by user at any time
|
||||
values.put(Calendars.VISIBLE, 1);
|
||||
values.put(Calendars.SYNC_EVENTS, 1);
|
||||
|
||||
return create(account, provider, values);
|
||||
}
|
||||
|
||||
public void update(CollectionInfo info) throws CalendarStorageException {
|
||||
update(valuesFromCollectionInfo(info));
|
||||
public void update(CollectionInfo info, boolean updateColor) throws CalendarStorageException {
|
||||
update(valuesFromCollectionInfo(info, updateColor));
|
||||
}
|
||||
|
||||
@TargetApi(15)
|
||||
private static ContentValues valuesFromCollectionInfo(CollectionInfo info) {
|
||||
private static ContentValues valuesFromCollectionInfo(CollectionInfo info, boolean withColor) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(Calendars.NAME, info.url);
|
||||
values.put(Calendars.CALENDAR_DISPLAY_NAME, info.displayName);
|
||||
values.put(Calendars.CALENDAR_COLOR, info.color != null ? info.color : defaultColor);
|
||||
values.put(Calendars.CALENDAR_DISPLAY_NAME, !TextUtils.isEmpty(info.displayName) ? info.displayName : DavUtils.lastSegmentOfUrl(info.url));
|
||||
|
||||
if (withColor)
|
||||
values.put(Calendars.CALENDAR_COLOR, info.color != null ? info.color : defaultColor);
|
||||
|
||||
if (info.readOnly)
|
||||
values.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_READ);
|
||||
@@ -100,8 +107,6 @@ public class LocalCalendar extends AndroidCalendar implements LocalCollection {
|
||||
values.put(Calendars.CAN_ORGANIZER_RESPOND, 1);
|
||||
}
|
||||
|
||||
values.put(Calendars.SYNC_EVENTS, 1);
|
||||
values.put(Calendars.VISIBLE, 1);
|
||||
if (!TextUtils.isEmpty(info.timeZone)) {
|
||||
VTimeZone timeZone = DateUtils.parseVTimeZone(info.timeZone);
|
||||
if (timeZone != null && timeZone.getTimeZoneId() != null)
|
||||
|
||||
@@ -13,16 +13,20 @@ import android.content.ContentValues;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
|
||||
import android.provider.ContactsContract.RawContacts.Data;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import at.bitfire.davdroid.App;
|
||||
import at.bitfire.davdroid.BuildConfig;
|
||||
import at.bitfire.davdroid.model.UnknownProperties;
|
||||
import at.bitfire.vcard4android.AndroidAddressBook;
|
||||
import at.bitfire.vcard4android.AndroidContact;
|
||||
import at.bitfire.vcard4android.AndroidContactFactory;
|
||||
import at.bitfire.vcard4android.BatchOperation;
|
||||
import at.bitfire.vcard4android.CachedGroupMembership;
|
||||
import at.bitfire.vcard4android.Contact;
|
||||
import at.bitfire.vcard4android.ContactsStorageException;
|
||||
import ezvcard.Ezvcard;
|
||||
@@ -32,6 +36,11 @@ public class LocalContact extends AndroidContact implements LocalResource {
|
||||
Contact.productID = "+//IDN bitfire.at//DAVdroid/" + BuildConfig.VERSION_NAME + " vcard4android ez-vcard/" + Ezvcard.VERSION;
|
||||
}
|
||||
|
||||
protected final Set<Long>
|
||||
cachedGroupMemberships = new HashSet<>(),
|
||||
groupMemberships = new HashSet<>();
|
||||
|
||||
|
||||
protected LocalContact(AndroidAddressBook addressBook, long id, String fileName, String eTag) {
|
||||
super(addressBook, id, fileName, eTag);
|
||||
}
|
||||
@@ -69,53 +78,96 @@ public class LocalContact extends AndroidContact implements LocalResource {
|
||||
}
|
||||
|
||||
|
||||
// group support
|
||||
|
||||
@Override
|
||||
protected void populateGroupMembership(ContentValues row) {
|
||||
if (row.containsKey(GroupMembership.GROUP_ROW_ID)) {
|
||||
long groupId = row.getAsLong(GroupMembership.GROUP_ROW_ID);
|
||||
|
||||
// fetch group
|
||||
LocalGroup group = new LocalGroup(addressBook, groupId);
|
||||
try {
|
||||
Contact groupInfo = group.getContact();
|
||||
|
||||
// add to CATEGORIES
|
||||
contact.getCategories().add(groupInfo.displayName);
|
||||
} catch (FileNotFoundException|ContactsStorageException e) {
|
||||
App.log.log(Level.WARNING, "Couldn't find assigned group #" + groupId + ", ignoring membership", e);
|
||||
}
|
||||
protected void populateData(String mimeType, ContentValues row) {
|
||||
switch (mimeType) {
|
||||
case CachedGroupMembership.CONTENT_ITEM_TYPE:
|
||||
cachedGroupMemberships.add(row.getAsLong(CachedGroupMembership.GROUP_ID));
|
||||
break;
|
||||
case GroupMembership.CONTENT_ITEM_TYPE:
|
||||
groupMemberships.add(row.getAsLong(GroupMembership.GROUP_ROW_ID));
|
||||
break;
|
||||
case UnknownProperties.CONTENT_ITEM_TYPE:
|
||||
contact.unknownProperties = row.getAsString(UnknownProperties.UNKNOWN_PROPERTIES);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void insertGroupMemberships(BatchOperation batch) throws ContactsStorageException {
|
||||
for (String category : contact.getCategories()) {
|
||||
// Is there already a category with this display name?
|
||||
LocalGroup group = ((LocalAddressBook)addressBook).findGroupByTitle(category);
|
||||
protected void insertDataRows(BatchOperation batch) throws ContactsStorageException {
|
||||
super.insertDataRows(batch);
|
||||
|
||||
if (group == null) {
|
||||
// no, we have to create the group before inserting the membership
|
||||
|
||||
Contact groupInfo = new Contact();
|
||||
groupInfo.displayName = category;
|
||||
group = new LocalGroup(addressBook, groupInfo);
|
||||
group.create();
|
||||
}
|
||||
|
||||
Long groupId = group.getId();
|
||||
if (groupId != null) {
|
||||
ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(dataSyncURI());
|
||||
if (id == null)
|
||||
builder.withValueBackReference(GroupMembership.RAW_CONTACT_ID, 0);
|
||||
else
|
||||
builder.withValue(GroupMembership.RAW_CONTACT_ID, id);
|
||||
builder .withValue(GroupMembership.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE)
|
||||
.withValue(GroupMembership.GROUP_ROW_ID, groupId);
|
||||
batch.enqueue(builder.build());
|
||||
}
|
||||
if (contact.unknownProperties != null) {
|
||||
ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(dataSyncURI());
|
||||
if (id == null)
|
||||
builder.withValueBackReference(UnknownProperties.RAW_CONTACT_ID, 0);
|
||||
else
|
||||
builder.withValue(UnknownProperties.RAW_CONTACT_ID, id);
|
||||
builder .withValue(UnknownProperties.MIMETYPE, UnknownProperties.CONTENT_ITEM_TYPE)
|
||||
.withValue(UnknownProperties.UNKNOWN_PROPERTIES, contact.unknownProperties);
|
||||
batch.enqueue(builder.build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void addToGroup(BatchOperation batch, long groupID) {
|
||||
assertID();
|
||||
batch.enqueue(ContentProviderOperation
|
||||
.newInsert(dataSyncURI())
|
||||
.withValue(GroupMembership.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE)
|
||||
.withValue(GroupMembership.RAW_CONTACT_ID, id)
|
||||
.withValue(GroupMembership.GROUP_ROW_ID, groupID)
|
||||
.build()
|
||||
);
|
||||
|
||||
batch.enqueue(ContentProviderOperation
|
||||
.newInsert(dataSyncURI())
|
||||
.withValue(CachedGroupMembership.MIMETYPE, CachedGroupMembership.CONTENT_ITEM_TYPE)
|
||||
.withValue(CachedGroupMembership.RAW_CONTACT_ID, id)
|
||||
.withValue(CachedGroupMembership.GROUP_ID, groupID)
|
||||
.withYieldAllowed(true)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
public void removeGroupMemberships(BatchOperation batch) {
|
||||
assertID();
|
||||
batch.enqueue(ContentProviderOperation
|
||||
.newDelete(dataSyncURI())
|
||||
.withSelection(
|
||||
Data.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + " IN (?,?)",
|
||||
new String[] { String.valueOf(id), GroupMembership.CONTENT_ITEM_TYPE, CachedGroupMembership.CONTENT_ITEM_TYPE }
|
||||
)
|
||||
.withYieldAllowed(true)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the IDs of all groups the contact was member of (cached memberships).
|
||||
* Cached memberships are kept in sync with memberships by DAVdroid and are used to determine
|
||||
* whether a membership has been deleted/added when a raw contact is dirty.
|
||||
* @return set of {@link GroupMembership#GROUP_ROW_ID} (may be empty)
|
||||
* @throws ContactsStorageException on contact provider errors
|
||||
* @throws FileNotFoundException if the current contact can't be found
|
||||
*/
|
||||
@NonNull
|
||||
public Set<Long> getCachedGroupMemberships() throws ContactsStorageException, FileNotFoundException {
|
||||
getContact();
|
||||
return cachedGroupMemberships;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the IDs of all groups the contact is member of.
|
||||
* @return set of {@link GroupMembership#GROUP_ROW_ID}s (may be empty)
|
||||
* @throws ContactsStorageException on contact provider errors
|
||||
* @throws FileNotFoundException if the current contact can't be found
|
||||
*/
|
||||
@NonNull
|
||||
public Set<Long> getGroupMemberships() throws ContactsStorageException, FileNotFoundException {
|
||||
getContact();
|
||||
return groupMemberships;
|
||||
}
|
||||
|
||||
|
||||
@@ -134,6 +186,7 @@ public class LocalContact extends AndroidContact implements LocalResource {
|
||||
return new LocalContact(addressBook, contact, fileName, eTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalContact[] newArray(int size) {
|
||||
return new LocalContact[size];
|
||||
}
|
||||
|
||||
@@ -8,28 +8,246 @@
|
||||
|
||||
package at.bitfire.davdroid.resource;
|
||||
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
|
||||
import android.provider.ContactsContract.Groups;
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
import android.provider.ContactsContract.RawContacts.Data;
|
||||
import android.provider.ContactsContract.RawContactsEntity;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import at.bitfire.dav4android.Constants;
|
||||
import at.bitfire.vcard4android.AndroidAddressBook;
|
||||
import at.bitfire.vcard4android.AndroidGroup;
|
||||
import at.bitfire.vcard4android.AndroidGroupFactory;
|
||||
import at.bitfire.vcard4android.BatchOperation;
|
||||
import at.bitfire.vcard4android.CachedGroupMembership;
|
||||
import at.bitfire.vcard4android.Contact;
|
||||
import at.bitfire.vcard4android.ContactsStorageException;
|
||||
import lombok.Cleanup;
|
||||
import lombok.ToString;
|
||||
|
||||
public class LocalGroup extends AndroidGroup {
|
||||
@ToString(callSuper=true)
|
||||
public class LocalGroup extends AndroidGroup implements LocalResource {
|
||||
/** marshalled list of member UIDs, as sent by server */
|
||||
public static final String COLUMN_PENDING_MEMBERS = Groups.SYNC3;
|
||||
|
||||
public LocalGroup(AndroidAddressBook addressBook, long id) {
|
||||
super(addressBook, id);
|
||||
public LocalGroup(AndroidAddressBook addressBook, long id, String fileName, String eTag) {
|
||||
super(addressBook, id, fileName, eTag);
|
||||
}
|
||||
|
||||
public LocalGroup(AndroidAddressBook addressBook, Contact contact) {
|
||||
super(addressBook, contact);
|
||||
public LocalGroup(AndroidAddressBook addressBook, Contact contact, String fileName, String eTag) {
|
||||
super(addressBook, contact, fileName, eTag);
|
||||
}
|
||||
|
||||
public void clearDirty() throws ContactsStorageException {
|
||||
ContentValues values = new ContentValues(1);
|
||||
values.put(ContactsContract.Groups.DIRTY, 0);
|
||||
|
||||
@Override
|
||||
public void clearDirty(String eTag) throws ContactsStorageException {
|
||||
assertID();
|
||||
|
||||
ContentValues values = new ContentValues(2);
|
||||
values.put(Groups.DIRTY, 0);
|
||||
values.put(COLUMN_ETAG, this.eTag = eTag);
|
||||
update(values);
|
||||
|
||||
// update cached group memberships
|
||||
BatchOperation batch = new BatchOperation(addressBook.provider);
|
||||
|
||||
// delete cached group memberships
|
||||
batch.enqueue(ContentProviderOperation
|
||||
.newDelete(addressBook.syncAdapterURI(ContactsContract.Data.CONTENT_URI))
|
||||
.withSelection(
|
||||
CachedGroupMembership.MIMETYPE + "=? AND " + CachedGroupMembership.GROUP_ID + "=?",
|
||||
new String[] { CachedGroupMembership.CONTENT_ITEM_TYPE, String.valueOf(id) }
|
||||
).build()
|
||||
);
|
||||
|
||||
// insert updated cached group memberships
|
||||
for (long member : getMembers())
|
||||
batch.enqueue(ContentProviderOperation
|
||||
.newInsert(addressBook.syncAdapterURI(ContactsContract.Data.CONTENT_URI))
|
||||
.withValue(CachedGroupMembership.MIMETYPE, CachedGroupMembership.CONTENT_ITEM_TYPE)
|
||||
.withValue(CachedGroupMembership.RAW_CONTACT_ID, member)
|
||||
.withValue(CachedGroupMembership.GROUP_ID, id)
|
||||
.withYieldAllowed(true)
|
||||
.build()
|
||||
);
|
||||
|
||||
batch.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFileNameAndUID(String uid) throws ContactsStorageException {
|
||||
String newFileName = uid + ".vcf";
|
||||
|
||||
ContentValues values = new ContentValues(2);
|
||||
values.put(COLUMN_FILENAME, newFileName);
|
||||
values.put(COLUMN_UID, uid);
|
||||
update(values);
|
||||
|
||||
fileName = newFileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ContentValues contentValues() {
|
||||
ContentValues values = super.contentValues();
|
||||
|
||||
@Cleanup("recycle") Parcel members = Parcel.obtain();
|
||||
members.writeStringList(contact.members);
|
||||
values.put(COLUMN_PENDING_MEMBERS, members.marshall());
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Marks all members of the current group as dirty.
|
||||
*/
|
||||
public void markMembersDirty() throws ContactsStorageException {
|
||||
assertID();
|
||||
BatchOperation batch = new BatchOperation(addressBook.provider);
|
||||
|
||||
for (long member : getMembers())
|
||||
batch.enqueue(ContentProviderOperation
|
||||
.newUpdate(addressBook.syncAdapterURI(ContentUris.withAppendedId(RawContacts.CONTENT_URI, member)))
|
||||
.withValue(RawContacts.DIRTY, 1)
|
||||
.withYieldAllowed(true)
|
||||
.build()
|
||||
);
|
||||
|
||||
batch.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes all groups with non-null {@link #COLUMN_PENDING_MEMBERS}: the pending memberships
|
||||
* are (if possible) applied, keeping cached memberships in sync.
|
||||
* @param addressBook address book to take groups from
|
||||
* @throws ContactsStorageException on contact provider errors
|
||||
*/
|
||||
public static void applyPendingMemberships(LocalAddressBook addressBook) throws ContactsStorageException {
|
||||
try {
|
||||
@Cleanup Cursor cursor = addressBook.provider.query(
|
||||
addressBook.syncAdapterURI(Groups.CONTENT_URI),
|
||||
new String[] { Groups._ID, COLUMN_PENDING_MEMBERS },
|
||||
COLUMN_PENDING_MEMBERS + " IS NOT NULL", new String[] {},
|
||||
null
|
||||
);
|
||||
|
||||
BatchOperation batch = new BatchOperation(addressBook.provider);
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
long id = cursor.getLong(0);
|
||||
Constants.log.fine("Assigning members to group " + id);
|
||||
|
||||
// delete all memberships and cached memberships for this group
|
||||
batch.enqueue(ContentProviderOperation
|
||||
.newDelete(addressBook.syncAdapterURI(ContactsContract.Data.CONTENT_URI))
|
||||
.withSelection(
|
||||
"(" + GroupMembership.MIMETYPE + "=? AND " + GroupMembership.GROUP_ROW_ID + "=?) OR (" +
|
||||
CachedGroupMembership.MIMETYPE + "=? AND " + CachedGroupMembership.GROUP_ID + "=?)",
|
||||
new String[] { GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(id), CachedGroupMembership.CONTENT_ITEM_TYPE, String.valueOf(id) })
|
||||
.withYieldAllowed(true)
|
||||
.build()
|
||||
);
|
||||
|
||||
// extract list of member UIDs
|
||||
List<String> members = new LinkedList<>();
|
||||
byte[] raw = cursor.getBlob(1);
|
||||
@Cleanup("recycle") Parcel parcel = Parcel.obtain();
|
||||
parcel.unmarshall(raw, 0, raw.length);
|
||||
parcel.setDataPosition(0);
|
||||
parcel.readStringList(members);
|
||||
|
||||
// insert memberships
|
||||
for (String uid : members) {
|
||||
Constants.log.fine("Assigning member: " + uid);
|
||||
try {
|
||||
LocalContact member = addressBook.findContactByUID(uid);
|
||||
member.addToGroup(batch, id);
|
||||
} catch(FileNotFoundException e) {
|
||||
Constants.log.log(Level.WARNING, "Group member not found: " + uid, e);
|
||||
}
|
||||
}
|
||||
|
||||
// remove pending memberships
|
||||
batch.enqueue(ContentProviderOperation
|
||||
.newUpdate(addressBook.syncAdapterURI(ContentUris.withAppendedId(Groups.CONTENT_URI, id)))
|
||||
.withValue(COLUMN_PENDING_MEMBERS, null)
|
||||
.withYieldAllowed(true)
|
||||
.build()
|
||||
);
|
||||
|
||||
batch.commit();
|
||||
}
|
||||
} catch(RemoteException e) {
|
||||
throw new ContactsStorageException("Couldn't get pending memberships", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// helpers
|
||||
|
||||
private void assertID() {
|
||||
if (id == null)
|
||||
throw new IllegalStateException("Group has not been saved yet");
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all members of this group.
|
||||
* @return list of all members' raw contact IDs
|
||||
* @throws ContactsStorageException on contact provider errors
|
||||
*/
|
||||
protected long[] getMembers() throws ContactsStorageException {
|
||||
assertID();
|
||||
List<Long> members = new LinkedList<>();
|
||||
try {
|
||||
@Cleanup Cursor cursor = addressBook.provider.query(
|
||||
addressBook.syncAdapterURI(ContactsContract.Data.CONTENT_URI),
|
||||
new String[] { Data.RAW_CONTACT_ID },
|
||||
GroupMembership.MIMETYPE + "=? AND " + GroupMembership.GROUP_ROW_ID + "=?",
|
||||
new String[] { GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(id) },
|
||||
null
|
||||
);
|
||||
while (cursor != null && cursor.moveToNext())
|
||||
members.add(cursor.getLong(0));
|
||||
} catch(RemoteException e) {
|
||||
throw new ContactsStorageException("Couldn't list group members", e);
|
||||
}
|
||||
return ArrayUtils.toPrimitive(members.toArray(new Long[members.size()]));
|
||||
}
|
||||
|
||||
|
||||
// factory
|
||||
|
||||
static class Factory extends AndroidGroupFactory {
|
||||
static final Factory INSTANCE = new Factory();
|
||||
|
||||
@Override
|
||||
public LocalGroup newInstance(AndroidAddressBook addressBook, long id, String fileName, String eTag) {
|
||||
return new LocalGroup(addressBook, id, fileName, eTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalGroup newInstance(AndroidAddressBook addressBook, Contact contact, String fileName, String eTag) {
|
||||
return new LocalGroup(addressBook, contact, fileName, eTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalGroup[] newArray(int size) {
|
||||
return new LocalGroup[size];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,18 +9,21 @@
|
||||
package at.bitfire.davdroid.resource;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.RemoteException;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.dmfs.provider.tasks.TaskContract.TaskLists;
|
||||
import org.dmfs.provider.tasks.TaskContract.Tasks;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
import at.bitfire.davdroid.DavUtils;
|
||||
import at.bitfire.davdroid.model.CollectionInfo;
|
||||
import at.bitfire.ical4android.AndroidTaskList;
|
||||
import at.bitfire.ical4android.AndroidTaskListFactory;
|
||||
@@ -55,22 +58,25 @@ public class LocalTaskList extends AndroidTaskList implements LocalCollection {
|
||||
}
|
||||
|
||||
public static Uri create(Account account, TaskProvider provider, CollectionInfo info) throws CalendarStorageException {
|
||||
ContentValues values = valuesFromCollectionInfo(info);
|
||||
ContentValues values = valuesFromCollectionInfo(info, true);
|
||||
values.put(TaskLists.OWNER, account.name);
|
||||
values.put(TaskLists.SYNC_ENABLED, 1);
|
||||
values.put(TaskLists.VISIBLE, 1);
|
||||
return create(account, provider, values);
|
||||
}
|
||||
|
||||
public void update(CollectionInfo info) throws CalendarStorageException {
|
||||
update(valuesFromCollectionInfo(info));
|
||||
public void update(CollectionInfo info, boolean updateColor) throws CalendarStorageException {
|
||||
update(valuesFromCollectionInfo(info, updateColor));
|
||||
}
|
||||
|
||||
private static ContentValues valuesFromCollectionInfo(CollectionInfo info) {
|
||||
private static ContentValues valuesFromCollectionInfo(CollectionInfo info, boolean withColor) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(TaskLists._SYNC_ID, info.url);
|
||||
values.put(TaskLists.LIST_NAME, info.displayName);
|
||||
values.put(TaskLists.LIST_COLOR, info.color != null ? info.color : defaultColor);
|
||||
values.put(TaskLists.SYNC_ENABLED, 1);
|
||||
values.put(TaskLists.VISIBLE, 1);
|
||||
values.put(TaskLists.LIST_NAME, !TextUtils.isEmpty(info.displayName) ? info.displayName : DavUtils.lastSegmentOfUrl(info.url));
|
||||
|
||||
if (withColor)
|
||||
values.put(TaskLists.LIST_COLOR, info.color != null ? info.color : defaultColor);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
@@ -131,12 +137,16 @@ public class LocalTaskList extends AndroidTaskList implements LocalCollection {
|
||||
|
||||
// helpers
|
||||
|
||||
public static boolean tasksProviderAvailable(@NonNull ContentResolver resolver) {
|
||||
public static boolean tasksProviderAvailable(@NonNull Context context) {
|
||||
if (tasksProviderAvailable != null)
|
||||
return tasksProviderAvailable;
|
||||
else {
|
||||
@Cleanup TaskProvider provider = TaskProvider.acquire(resolver, TaskProvider.ProviderName.OpenTasks);
|
||||
return tasksProviderAvailable = (provider != null);
|
||||
if (Build.VERSION.SDK_INT >= 23)
|
||||
return context.getPackageManager().resolveContentProvider(TaskProvider.ProviderName.OpenTasks.authority, 0) != null;
|
||||
else {
|
||||
@Cleanup TaskProvider provider = TaskProvider.acquire(context.getContentResolver(), TaskProvider.ProviderName.OpenTasks);
|
||||
return tasksProviderAvailable = (provider != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,17 +9,15 @@
|
||||
package at.bitfire.davdroid.syncadapter;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.SyncResult;
|
||||
import android.os.Bundle;
|
||||
import android.provider.CalendarContract.Calendars;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.apache.commons.codec.Charsets;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
@@ -34,12 +32,11 @@ import at.bitfire.dav4android.DavCalendar;
|
||||
import at.bitfire.dav4android.DavResource;
|
||||
import at.bitfire.dav4android.exception.DavException;
|
||||
import at.bitfire.dav4android.exception.HttpException;
|
||||
import at.bitfire.dav4android.property.CalendarColor;
|
||||
import at.bitfire.dav4android.property.CalendarData;
|
||||
import at.bitfire.dav4android.property.DisplayName;
|
||||
import at.bitfire.dav4android.property.GetCTag;
|
||||
import at.bitfire.dav4android.property.GetContentType;
|
||||
import at.bitfire.dav4android.property.GetETag;
|
||||
import at.bitfire.davdroid.AccountSettings;
|
||||
import at.bitfire.davdroid.App;
|
||||
import at.bitfire.davdroid.ArrayUtils;
|
||||
import at.bitfire.davdroid.Constants;
|
||||
@@ -58,21 +55,30 @@ import okhttp3.MediaType;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
/**
|
||||
* Synchronization manager for CalDAV collections; handles events ({@code VEVENT}).
|
||||
*/
|
||||
public class CalendarSyncManager extends SyncManager {
|
||||
|
||||
protected static final int MAX_MULTIGET = 20;
|
||||
|
||||
|
||||
public CalendarSyncManager(Context context, Account account, Bundle extras, String authority, SyncResult result, LocalCalendar calendar) throws InvalidAccountException {
|
||||
super(Constants.NOTIFICATION_CALENDAR_SYNC, context, account, extras, authority, result);
|
||||
public CalendarSyncManager(Context context, Account account, AccountSettings settings, Bundle extras, String authority, SyncResult result, LocalCalendar calendar) throws InvalidAccountException {
|
||||
super(context, account, settings, extras, authority, result, "calendar/" + calendar.getId());
|
||||
localCollection = calendar;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int notificationId() {
|
||||
return Constants.NOTIFICATION_CALENDAR_SYNC;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSyncErrorTitle() {
|
||||
return context.getString(R.string.sync_error_calendar, account.name);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void prepare() {
|
||||
collectionURL = HttpUrl.parse(localCalendar().getName());
|
||||
@@ -94,9 +100,14 @@ public class CalendarSyncManager extends SyncManager {
|
||||
@Override
|
||||
protected RequestBody prepareUpload(LocalResource resource) throws IOException, CalendarStorageException {
|
||||
LocalEvent local = (LocalEvent)resource;
|
||||
App.log.log(Level.FINE, "Preparing upload of event " + local.getFileName(), local.getEvent());
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
local.getEvent().write(os);
|
||||
|
||||
return RequestBody.create(
|
||||
DavCalendar.MIME_ICALENDAR,
|
||||
local.getEvent().toStream().toByteArray()
|
||||
os.toByteArray()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -137,7 +148,11 @@ public class CalendarSyncManager extends SyncManager {
|
||||
DavResource remote = bunch[0];
|
||||
|
||||
ResponseBody body = remote.get("text/calendar");
|
||||
String eTag = ((GetETag)remote.properties.get(GetETag.NAME)).eTag;
|
||||
|
||||
// CalDAV servers MUST return ETag on GET [https://tools.ietf.org/html/rfc4791#section-5.3.4]
|
||||
GetETag eTag = (GetETag)remote.properties.get(GetETag.NAME);
|
||||
if (eTag == null || StringUtils.isEmpty(eTag.eTag))
|
||||
throw new DavException("Received CalDAV GET response without ETag for " + remote.location);
|
||||
|
||||
Charset charset = Charsets.UTF_8;
|
||||
MediaType contentType = body.contentType();
|
||||
@@ -145,7 +160,7 @@ public class CalendarSyncManager extends SyncManager {
|
||||
charset = contentType.charset(Charsets.UTF_8);
|
||||
|
||||
@Cleanup InputStream stream = body.byteStream();
|
||||
processVEvent(remote.fileName(), eTag, stream, charset);
|
||||
processVEvent(remote.fileName(), eTag.eTag, stream, charset);
|
||||
|
||||
} else {
|
||||
// multiple contacts, use multi-get
|
||||
|
||||
@@ -8,18 +8,17 @@
|
||||
package at.bitfire.davdroid.syncadapter;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.app.Service;
|
||||
import android.content.AbstractThreadedSyncAdapter;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SyncResult;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.provider.CalendarContract;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
@@ -28,11 +27,12 @@ import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import at.bitfire.davdroid.AccountSettings;
|
||||
import at.bitfire.davdroid.App;
|
||||
import at.bitfire.davdroid.InvalidAccountException;
|
||||
import at.bitfire.davdroid.model.CollectionInfo;
|
||||
import at.bitfire.davdroid.model.ServiceDB;
|
||||
import at.bitfire.davdroid.model.ServiceDB.Collections;
|
||||
import at.bitfire.davdroid.model.ServiceDB.OpenHelper;
|
||||
import at.bitfire.davdroid.model.ServiceDB.Services;
|
||||
import at.bitfire.davdroid.resource.LocalCalendar;
|
||||
import at.bitfire.ical4android.CalendarStorageException;
|
||||
@@ -41,16 +41,15 @@ import lombok.Cleanup;
|
||||
public class CalendarsSyncAdapterService extends SyncAdapterService {
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
syncAdapter = new SyncAdapter(this, dbHelper);
|
||||
protected AbstractThreadedSyncAdapter syncAdapter() {
|
||||
return new SyncAdapter(this);
|
||||
}
|
||||
|
||||
|
||||
private static class SyncAdapter extends SyncAdapterService.SyncAdapter {
|
||||
|
||||
public SyncAdapter(Context context, OpenHelper dbHelper) {
|
||||
super(context, dbHelper);
|
||||
public SyncAdapter(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -58,11 +57,15 @@ public class CalendarsSyncAdapterService extends SyncAdapterService {
|
||||
super.onPerformSync(account, extras, authority, provider, syncResult);
|
||||
|
||||
try {
|
||||
updateLocalCalendars(provider, account);
|
||||
AccountSettings settings = new AccountSettings(getContext(), account);
|
||||
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(settings))
|
||||
return;
|
||||
|
||||
updateLocalCalendars(provider, account, settings);
|
||||
|
||||
for (LocalCalendar calendar : (LocalCalendar[])LocalCalendar.find(account, provider, LocalCalendar.Factory.INSTANCE, CalendarContract.Calendars.SYNC_EVENTS + "!=0", null)) {
|
||||
App.log.info("Synchronizing calendar #" + calendar.getId() + ", URL: " + calendar.getName());
|
||||
CalendarSyncManager syncManager = new CalendarSyncManager(getContext(), account, extras, authority, syncResult, calendar);
|
||||
CalendarSyncManager syncManager = new CalendarSyncManager(getContext(), account, settings, extras, authority, syncResult, calendar);
|
||||
syncManager.performSync();
|
||||
}
|
||||
} catch (CalendarStorageException e) {
|
||||
@@ -75,38 +78,47 @@ public class CalendarsSyncAdapterService extends SyncAdapterService {
|
||||
App.log.info("Calendar sync complete");
|
||||
}
|
||||
|
||||
private void updateLocalCalendars(ContentProviderClient provider, Account account) throws CalendarStorageException {
|
||||
// enumerate remote and local calendars
|
||||
Long service = getService(account);
|
||||
Map<String, CollectionInfo> remote = remoteCalendars(service);
|
||||
LocalCalendar[] local = (LocalCalendar[])LocalCalendar.find(account, provider, LocalCalendar.Factory.INSTANCE, null, null);
|
||||
private void updateLocalCalendars(ContentProviderClient provider, Account account, AccountSettings settings) throws CalendarStorageException {
|
||||
SQLiteOpenHelper dbHelper = new ServiceDB.OpenHelper(getContext());
|
||||
try {
|
||||
// enumerate remote and local calendars
|
||||
SQLiteDatabase db = dbHelper.getReadableDatabase();
|
||||
Long service = getService(db, account);
|
||||
Map<String, CollectionInfo> remote = remoteCalendars(db, service);
|
||||
|
||||
// delete obsolete local calendar
|
||||
for (LocalCalendar calendar : local) {
|
||||
String url = calendar.getName();
|
||||
if (!remote.containsKey(url)) {
|
||||
App.log.fine("Deleting obsolete local calendar " + url);
|
||||
calendar.delete();
|
||||
} else {
|
||||
// remote CollectionInfo found for this local collection, update data
|
||||
CollectionInfo info = remote.get(url);
|
||||
App.log.fine("Updating local calendar " + url + " with " + info);
|
||||
calendar.update(info);
|
||||
// we already have a local calendar for this remote collection, don't take into consideration anymore
|
||||
remote.remove(url);
|
||||
LocalCalendar[] local = (LocalCalendar[])LocalCalendar.find(account, provider, LocalCalendar.Factory.INSTANCE, null, null);
|
||||
|
||||
boolean updateColors = settings.getManageCalendarColors();
|
||||
|
||||
// delete obsolete local calendar
|
||||
for (LocalCalendar calendar : local) {
|
||||
String url = calendar.getName();
|
||||
if (!remote.containsKey(url)) {
|
||||
App.log.fine("Deleting obsolete local calendar " + url);
|
||||
calendar.delete();
|
||||
} else {
|
||||
// remote CollectionInfo found for this local collection, update data
|
||||
CollectionInfo info = remote.get(url);
|
||||
App.log.fine("Updating local calendar " + url + " with " + info);
|
||||
calendar.update(info, updateColors);
|
||||
// we already have a local calendar for this remote collection, don't take into consideration anymore
|
||||
remote.remove(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create new local calendars
|
||||
for (String url : remote.keySet()) {
|
||||
CollectionInfo info = remote.get(url);
|
||||
App.log.info("Adding local calendar list " + info);
|
||||
LocalCalendar.create(account, provider, info);
|
||||
// create new local calendars
|
||||
for (String url : remote.keySet()) {
|
||||
CollectionInfo info = remote.get(url);
|
||||
App.log.info("Adding local calendar list " + info);
|
||||
LocalCalendar.create(account, provider, info);
|
||||
}
|
||||
} finally {
|
||||
dbHelper.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Long getService(Account account) {
|
||||
Long getService(@NonNull SQLiteDatabase db, @NonNull Account account) {
|
||||
@Cleanup Cursor c = db.query(Services._TABLE, new String[] { Services.ID },
|
||||
Services.ACCOUNT_NAME + "=? AND " + Services.SERVICE + "=?", new String[] { account.name, Services.SERVICE_CALDAV }, null, null, null);
|
||||
if (c.moveToNext())
|
||||
@@ -116,7 +128,7 @@ public class CalendarsSyncAdapterService extends SyncAdapterService {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Map<String, CollectionInfo> remoteCalendars(Long service) {
|
||||
private Map<String, CollectionInfo> remoteCalendars(@NonNull SQLiteDatabase db, Long service) {
|
||||
Map<String, CollectionInfo> collections = new LinkedHashMap<>();
|
||||
if (service != null) {
|
||||
@Cleanup Cursor cursor = db.query(Collections._TABLE, null,
|
||||
|
||||
@@ -8,22 +8,20 @@
|
||||
package at.bitfire.davdroid.syncadapter;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.app.Service;
|
||||
import android.content.AbstractThreadedSyncAdapter;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SyncResult;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import at.bitfire.davdroid.AccountSettings;
|
||||
@@ -32,48 +30,58 @@ import at.bitfire.davdroid.InvalidAccountException;
|
||||
import at.bitfire.davdroid.model.CollectionInfo;
|
||||
import at.bitfire.davdroid.model.ServiceDB;
|
||||
import at.bitfire.davdroid.model.ServiceDB.Collections;
|
||||
import at.bitfire.davdroid.model.ServiceDB.OpenHelper;
|
||||
import lombok.Cleanup;
|
||||
|
||||
public class ContactsSyncAdapterService extends SyncAdapterService {
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
syncAdapter = new ContactsSyncAdapter(this, dbHelper);
|
||||
}
|
||||
@Override
|
||||
protected AbstractThreadedSyncAdapter syncAdapter() {
|
||||
return new ContactsSyncAdapter(this);
|
||||
}
|
||||
|
||||
|
||||
private static class ContactsSyncAdapter extends SyncAdapter {
|
||||
|
||||
public ContactsSyncAdapter(Context context, OpenHelper dbHelper) {
|
||||
super(context, dbHelper);
|
||||
public ContactsSyncAdapter(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
|
||||
super.onPerformSync(account, extras, authority, provider, syncResult);
|
||||
|
||||
Long service = getService(account);
|
||||
if (service != null) {
|
||||
CollectionInfo remote = remoteAddressBook(service);
|
||||
if (remote != null)
|
||||
try {
|
||||
ContactsSyncManager syncManager = new ContactsSyncManager(getContext(), account, extras, authority, provider, syncResult, remote);
|
||||
syncManager.performSync();
|
||||
} catch (InvalidAccountException e) {
|
||||
App.log.log(Level.SEVERE, "Couldn't get account settings", e);
|
||||
}
|
||||
else
|
||||
App.log.info("No address book collection selected for synchronization");
|
||||
} else
|
||||
App.log.info("No CardDAV service found in DB");
|
||||
SQLiteOpenHelper dbHelper = new ServiceDB.OpenHelper(getContext());
|
||||
try {
|
||||
AccountSettings settings = new AccountSettings(getContext(), account);
|
||||
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(settings))
|
||||
return;
|
||||
|
||||
SQLiteDatabase db = dbHelper.getReadableDatabase();
|
||||
Long service = getService(db, account);
|
||||
if (service != null) {
|
||||
CollectionInfo remote = remoteAddressBook(db, service);
|
||||
if (remote != null)
|
||||
try {
|
||||
ContactsSyncManager syncManager = new ContactsSyncManager(getContext(), account, settings, extras, authority, provider, syncResult, remote);
|
||||
syncManager.performSync();
|
||||
} catch(InvalidAccountException e) {
|
||||
App.log.log(Level.SEVERE, "Couldn't get account settings", e);
|
||||
}
|
||||
else
|
||||
App.log.info("No address book collection selected for synchronization");
|
||||
} else
|
||||
App.log.info("No CardDAV service found in DB");
|
||||
} catch (InvalidAccountException e) {
|
||||
App.log.log(Level.SEVERE, "Couldn't get account settings", e);
|
||||
} finally {
|
||||
dbHelper.close();
|
||||
}
|
||||
|
||||
App.log.info("Address book sync complete");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Long getService(@NonNull Account account) {
|
||||
private Long getService(@NonNull SQLiteDatabase db, @NonNull Account account) {
|
||||
@Cleanup Cursor c = db.query(ServiceDB.Services._TABLE, new String[] { ServiceDB.Services.ID },
|
||||
ServiceDB.Services.ACCOUNT_NAME + "=? AND " + ServiceDB.Services.SERVICE + "=?", new String[] { account.name, ServiceDB.Services.SERVICE_CARDDAV }, null, null, null);
|
||||
if (c.moveToNext())
|
||||
@@ -83,7 +91,7 @@ public class ContactsSyncAdapterService extends SyncAdapterService {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private CollectionInfo remoteAddressBook(long service) {
|
||||
private CollectionInfo remoteAddressBook(@NonNull SQLiteDatabase db, long service) {
|
||||
@Cleanup Cursor c = db.query(Collections._TABLE, null,
|
||||
Collections.SERVICE_ID + "=? AND " + Collections.SYNC, new String[] { String.valueOf(service) }, null, null, null);
|
||||
if (c.moveToNext()) {
|
||||
|
||||
@@ -10,20 +10,33 @@ package at.bitfire.davdroid.syncadapter;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.SyncResult;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.ContactsContract.Groups;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.apache.commons.codec.Charsets;
|
||||
import org.apache.commons.collections4.SetUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import at.bitfire.dav4android.DavAddressBook;
|
||||
@@ -35,6 +48,7 @@ import at.bitfire.dav4android.property.GetCTag;
|
||||
import at.bitfire.dav4android.property.GetContentType;
|
||||
import at.bitfire.dav4android.property.GetETag;
|
||||
import at.bitfire.dav4android.property.SupportedAddressData;
|
||||
import at.bitfire.davdroid.AccountSettings;
|
||||
import at.bitfire.davdroid.App;
|
||||
import at.bitfire.davdroid.ArrayUtils;
|
||||
import at.bitfire.davdroid.Constants;
|
||||
@@ -47,8 +61,10 @@ import at.bitfire.davdroid.resource.LocalContact;
|
||||
import at.bitfire.davdroid.resource.LocalGroup;
|
||||
import at.bitfire.davdroid.resource.LocalResource;
|
||||
import at.bitfire.ical4android.CalendarStorageException;
|
||||
import at.bitfire.vcard4android.BatchOperation;
|
||||
import at.bitfire.vcard4android.Contact;
|
||||
import at.bitfire.vcard4android.ContactsStorageException;
|
||||
import at.bitfire.vcard4android.GroupMethod;
|
||||
import ezvcard.VCardVersion;
|
||||
import ezvcard.util.IOUtils;
|
||||
import lombok.Cleanup;
|
||||
@@ -61,20 +77,62 @@ import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
/**
|
||||
* <p>Synchronization manager for CardDAV collections; handles contacts and groups.</p>
|
||||
*
|
||||
* <p></p>Group handling differs according to the {@link #groupMethod}. There are two basic methods to
|
||||
* handle/manage groups:</p>
|
||||
* <ul>
|
||||
* <li>{@code CATEGORIES}: groups memberships are attached to each contact and represented as
|
||||
* "category". When a group is dirty or has been deleted, all its members have to be set to
|
||||
* dirty, too (because they have to be uploaded without the respective category). This
|
||||
* is done in {@link #prepareDirty()}. Empty groups can be deleted without further processing,
|
||||
* which is done in {@link #postProcess()} because groups may become empty after downloading
|
||||
* updated remoted contacts.</li>
|
||||
* <li>Groups as separate VCards: individual and group contacts (with a list of member UIDs) are
|
||||
* distinguished. When a local group is dirty, its members don't need to be set to dirty.
|
||||
* <ol>
|
||||
* <li>However, when a contact is dirty, it has
|
||||
* to be checked whether its group memberships have changed. In this case, the respective
|
||||
* groups have to be set to dirty. For instance, if contact A is in group G and H, and then
|
||||
* group membership of G is removed, the contact will be set to dirty because of the changed
|
||||
* {@link android.provider.ContactsContract.CommonDataKinds.GroupMembership}. DAVdroid will
|
||||
* then have to check whether the group memberships have actually changed, and if so,
|
||||
* all affected groups have to be set to dirty. To detect changes in group memberships,
|
||||
* DAVdroid always mirrors all {@link android.provider.ContactsContract.CommonDataKinds.GroupMembership}
|
||||
* data rows in respective {@link at.bitfire.vcard4android.CachedGroupMembership} rows.
|
||||
* If the cached group memberships are not the same as the current group member ships, the
|
||||
* difference set (in our example G, because its in the cached memberships, but not in the
|
||||
* actual ones) is marked as dirty. This is done in {@link #prepareDirty()}.</li>
|
||||
* <li>When downloading remote contacts, groups (+ member information) may be received
|
||||
* by the actual members. Thus, the member lists have to be cached until all VCards
|
||||
* are received. This is done by caching the member UIDs of each group in
|
||||
* {@link LocalGroup#COLUMN_PENDING_MEMBERS}. In {@link #postProcess()},
|
||||
* these "pending memberships" are assigned to the actual contacs and then cleaned up.</li>
|
||||
* </ol>
|
||||
* </ul>
|
||||
*/
|
||||
public class ContactsSyncManager extends SyncManager {
|
||||
protected static final int MAX_MULTIGET = 10;
|
||||
|
||||
final private ContentProviderClient provider;
|
||||
final private CollectionInfo remote;
|
||||
|
||||
private boolean hasVCard4;
|
||||
private GroupMethod groupMethod;
|
||||
|
||||
|
||||
public ContactsSyncManager(Context context, Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult result, CollectionInfo remote) throws InvalidAccountException {
|
||||
super(Constants.NOTIFICATION_CONTACTS_SYNC, context, account, extras, authority, result);
|
||||
public ContactsSyncManager(Context context, Account account, AccountSettings settings, Bundle extras, String authority, ContentProviderClient provider, SyncResult result, CollectionInfo remote) throws InvalidAccountException {
|
||||
super(context, account, settings, extras, authority, result, "addressBook");
|
||||
this.provider = provider;
|
||||
this.remote = remote;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int notificationId() {
|
||||
return Constants.NOTIFICATION_CONTACTS_SYNC;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSyncErrorTitle() {
|
||||
return context.getString(R.string.sync_error_contacts, account.name);
|
||||
@@ -85,39 +143,127 @@ public class ContactsSyncManager extends SyncManager {
|
||||
protected void prepare() throws ContactsStorageException {
|
||||
// prepare local address book
|
||||
localCollection = new LocalAddressBook(account, provider);
|
||||
LocalAddressBook localAddressBook = localAddressBook();
|
||||
|
||||
String url = remote.url;
|
||||
String lastUrl = localAddressBook().getURL();
|
||||
String lastUrl = localAddressBook.getURL();
|
||||
if (!url.equals(lastUrl)) {
|
||||
App.log.info("Selected address book has changed from " + lastUrl + " to " + url + ", deleting all local contacts");
|
||||
((LocalAddressBook)localCollection).deleteAll();
|
||||
localAddressBook.deleteAll();
|
||||
}
|
||||
|
||||
// set up Contacts Provider Settings
|
||||
ContentValues values = new ContentValues(2);
|
||||
values.put(ContactsContract.Settings.SHOULD_SYNC, 1);
|
||||
values.put(ContactsContract.Settings.UNGROUPED_VISIBLE, 1);
|
||||
localAddressBook.updateSettings(values);
|
||||
|
||||
collectionURL = HttpUrl.parse(url);
|
||||
davCollection = new DavAddressBook(httpClient, collectionURL);
|
||||
|
||||
processChangedGroups();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void queryCapabilities() throws DavException, IOException, HttpException {
|
||||
// prepare remote address book
|
||||
hasVCard4 = false;
|
||||
davCollection.propfind(0, SupportedAddressData.NAME, GetCTag.NAME);
|
||||
SupportedAddressData supportedAddressData = (SupportedAddressData) davCollection.properties.get(SupportedAddressData.NAME);
|
||||
if (supportedAddressData != null)
|
||||
for (MediaType type : supportedAddressData.types)
|
||||
if ("text/vcard; version=4.0".equalsIgnoreCase(type.toString()))
|
||||
hasVCard4 = true;
|
||||
SupportedAddressData supportedAddressData = (SupportedAddressData)davCollection.properties.get(SupportedAddressData.NAME);
|
||||
hasVCard4 = supportedAddressData != null && supportedAddressData.hasVCard4();
|
||||
App.log.info("Server advertises VCard/4 support: " + hasVCard4);
|
||||
|
||||
groupMethod = settings.getGroupMethod();
|
||||
App.log.info("Contact group method: " + groupMethod);
|
||||
|
||||
localAddressBook().includeGroups = groupMethod == GroupMethod.GROUP_VCARDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RequestBody prepareUpload(LocalResource resource) throws IOException, ContactsStorageException {
|
||||
LocalContact local = (LocalContact)resource;
|
||||
protected void prepareDirty() throws CalendarStorageException, ContactsStorageException {
|
||||
super.prepareDirty();
|
||||
|
||||
LocalAddressBook addressBook = localAddressBook();
|
||||
|
||||
if (groupMethod == GroupMethod.CATEGORIES) {
|
||||
/* groups memberships are represented as contact CATEGORIES */
|
||||
|
||||
// groups with DELETED=1: set all members to dirty, then remove group
|
||||
for (LocalGroup group : addressBook.getDeletedGroups()) {
|
||||
App.log.fine("Finally removing group " + group);
|
||||
// useless because Android deletes group memberships as soon as a group is set to DELETED:
|
||||
// group.markMembersDirty();
|
||||
group.delete();
|
||||
}
|
||||
|
||||
// groups with DIRTY=1: mark all memberships as dirty, then clean DIRTY flag of group
|
||||
for (LocalGroup group : addressBook.getDirtyGroups()) {
|
||||
App.log.fine("Marking members of modified group " + group + " as dirty");
|
||||
group.markMembersDirty();
|
||||
group.clearDirty(null);
|
||||
}
|
||||
} else {
|
||||
/* groups as separate VCards: there are group contacts and individual contacts */
|
||||
|
||||
// mark groups with changed members as dirty
|
||||
BatchOperation batch = new BatchOperation(addressBook.provider);
|
||||
for (LocalContact contact : addressBook.getDirtyContacts())
|
||||
try {
|
||||
App.log.fine("Looking for changed group memberships of contact " + contact.getFileName());
|
||||
Set<Long> cachedGroups = contact.getCachedGroupMemberships(),
|
||||
currentGroups = contact.getGroupMemberships();
|
||||
for (Long groupID : SetUtils.disjunction(cachedGroups, currentGroups)) {
|
||||
App.log.fine("Marking group as dirty: " + groupID);
|
||||
batch.enqueue(ContentProviderOperation
|
||||
.newUpdate(addressBook.syncAdapterURI(ContentUris.withAppendedId(Groups.CONTENT_URI, groupID)))
|
||||
.withValue(Groups.DIRTY, 1)
|
||||
.withYieldAllowed(true)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
} catch(FileNotFoundException ignored) {
|
||||
}
|
||||
batch.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RequestBody prepareUpload(@NonNull LocalResource resource) throws IOException, ContactsStorageException {
|
||||
final Contact contact;
|
||||
if (resource instanceof LocalContact) {
|
||||
LocalContact local = ((LocalContact)resource);
|
||||
contact = local.getContact();
|
||||
|
||||
if (groupMethod == GroupMethod.CATEGORIES) {
|
||||
// add groups as CATEGORIES
|
||||
for (long groupID : local.getGroupMemberships()) {
|
||||
try {
|
||||
@Cleanup Cursor c = provider.query(
|
||||
localAddressBook().syncAdapterURI(ContentUris.withAppendedId(Groups.CONTENT_URI, groupID)),
|
||||
new String[] { Groups.TITLE },
|
||||
null, null,
|
||||
null
|
||||
);
|
||||
if (c != null && c.moveToNext()) {
|
||||
String title = c.getString(0);
|
||||
if (!TextUtils.isEmpty(title))
|
||||
contact.categories.add(title);
|
||||
}
|
||||
} catch(RemoteException e) {
|
||||
throw new ContactsStorageException("Couldn't find group for adding CATEGORIES", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (resource instanceof LocalGroup)
|
||||
contact = ((LocalGroup)resource).getContact();
|
||||
else
|
||||
throw new IllegalArgumentException("Argument must be LocalContact or LocalGroup");
|
||||
|
||||
App.log.log(Level.FINE, "Preparing upload of VCard " + resource.getFileName(), contact);
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
contact.write(hasVCard4 ? VCardVersion.V4_0 : VCardVersion.V3_0, groupMethod, settings.getVCardRFC6868(), os);
|
||||
|
||||
return RequestBody.create(
|
||||
hasVCard4 ? DavAddressBook.MIME_VCARD4 : DavAddressBook.MIME_VCARD3_UTF8,
|
||||
local.getContact().toStream(hasVCard4 ? VCardVersion.V4_0 : VCardVersion.V3_0).toByteArray()
|
||||
os.toByteArray()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -169,7 +315,11 @@ public class ContactsSyncManager extends SyncManager {
|
||||
DavResource remote = bunch[0];
|
||||
|
||||
ResponseBody body = remote.get("text/vcard;version=4.0, text/vcard;charset=utf-8;q=0.8, text/vcard;q=0.5");
|
||||
String eTag = ((GetETag) remote.properties.get(GetETag.NAME)).eTag;
|
||||
|
||||
// CardDAV servers MUST return ETag on GET [https://tools.ietf.org/html/rfc6352#section-6.3.2.3]
|
||||
GetETag eTag = (GetETag)remote.properties.get(GetETag.NAME);
|
||||
if (eTag == null || StringUtils.isEmpty(eTag.eTag))
|
||||
throw new DavException("Received CardDAV GET response without ETag for " + remote.location);
|
||||
|
||||
Charset charset = Charsets.UTF_8;
|
||||
MediaType contentType = body.contentType();
|
||||
@@ -177,7 +327,7 @@ public class ContactsSyncManager extends SyncManager {
|
||||
charset = contentType.charset(Charsets.UTF_8);
|
||||
|
||||
@Cleanup InputStream stream = body.byteStream();
|
||||
processVCard(remote.fileName(), eTag, stream, charset, downloader);
|
||||
processVCard(remote.fileName(), eTag.eTag, stream, charset, downloader);
|
||||
|
||||
} else {
|
||||
// multiple contacts, use multi-get
|
||||
@@ -189,21 +339,21 @@ public class ContactsSyncManager extends SyncManager {
|
||||
// process multiget results
|
||||
for (DavResource remote : davCollection.members) {
|
||||
String eTag;
|
||||
GetETag getETag = (GetETag) remote.properties.get(GetETag.NAME);
|
||||
GetETag getETag = (GetETag)remote.properties.get(GetETag.NAME);
|
||||
if (getETag != null)
|
||||
eTag = getETag.eTag;
|
||||
else
|
||||
throw new DavException("Received multi-get response without ETag");
|
||||
|
||||
Charset charset = Charsets.UTF_8;
|
||||
GetContentType getContentType = (GetContentType) remote.properties.get(GetContentType.NAME);
|
||||
GetContentType getContentType = (GetContentType)remote.properties.get(GetContentType.NAME);
|
||||
if (getContentType != null && getContentType.type != null) {
|
||||
MediaType type = MediaType.parse(getContentType.type);
|
||||
if (type != null)
|
||||
charset = type.charset(Charsets.UTF_8);
|
||||
}
|
||||
|
||||
AddressData addressData = (AddressData) remote.properties.get(AddressData.NAME);
|
||||
AddressData addressData = (AddressData)remote.properties.get(AddressData.NAME);
|
||||
if (addressData == null || addressData.vCard == null)
|
||||
throw new DavException("Received multi-get response without address data");
|
||||
|
||||
@@ -214,6 +364,22 @@ public class ContactsSyncManager extends SyncManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postProcess() throws CalendarStorageException, ContactsStorageException {
|
||||
if (groupMethod == GroupMethod.CATEGORIES) {
|
||||
/* VCard3 group handling: groups memberships are represented as contact CATEGORIES */
|
||||
|
||||
// remove empty groups
|
||||
App.log.info("Removing empty groups");
|
||||
localAddressBook().removeEmptyGroups();
|
||||
|
||||
} else {
|
||||
/* VCard4 group handling: there are group contacts and individual contacts */
|
||||
App.log.info("Assigning memberships of downloaded contact groups");
|
||||
LocalGroup.applyPendingMemberships(localAddressBook());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveSyncState() throws CalendarStorageException, ContactsStorageException {
|
||||
super.saveSyncState();
|
||||
@@ -226,48 +392,85 @@ public class ContactsSyncManager extends SyncManager {
|
||||
private DavAddressBook davAddressBook() { return (DavAddressBook)davCollection; }
|
||||
private LocalAddressBook localAddressBook() { return (LocalAddressBook)localCollection; }
|
||||
|
||||
private void processChangedGroups() throws ContactsStorageException {
|
||||
LocalAddressBook addressBook = localAddressBook();
|
||||
|
||||
// groups with DELETED=1: remove group finally
|
||||
for (LocalGroup group : addressBook.getDeletedGroups()) {
|
||||
long groupId = group.getId();
|
||||
App.log.fine("Finally removing group #" + groupId);
|
||||
// remove group memberships, but not as sync adapter (should marks contacts as DIRTY)
|
||||
// NOTE: doesn't work that way because Contact Provider removes the group memberships even for DELETED groups
|
||||
// addressBook.removeGroupMemberships(groupId, false);
|
||||
group.delete();
|
||||
}
|
||||
|
||||
// groups with DIRTY=1: mark all memberships as dirty, then clean DIRTY flag of group
|
||||
for (LocalGroup group : addressBook.getDirtyGroups()) {
|
||||
long groupId = group.getId();
|
||||
App.log.fine("Marking members of modified group #" + groupId + " as dirty");
|
||||
addressBook.markMembersDirty(groupId);
|
||||
group.clearDirty();
|
||||
}
|
||||
}
|
||||
|
||||
private void processVCard(String fileName, String eTag, InputStream stream, Charset charset, Contact.Downloader downloader) throws IOException, ContactsStorageException {
|
||||
App.log.info("Processing CardDAV resource " + fileName);
|
||||
Contact[] contacts = Contact.fromStream(stream, charset, downloader);
|
||||
if (contacts.length == 1) {
|
||||
Contact newData = contacts[0];
|
||||
if (contacts.length == 0) {
|
||||
App.log.warning("Received VCard without data, ignoring");
|
||||
return;
|
||||
} else if (contacts.length > 1)
|
||||
App.log.warning("Received multiple VCards, using first one");
|
||||
|
||||
// update local contact, if it exists
|
||||
LocalContact localContact = (LocalContact)localResources.get(fileName);
|
||||
if (localContact != null) {
|
||||
App.log.info("Updating " + fileName + " in local address book");
|
||||
localContact.eTag = eTag;
|
||||
localContact.update(newData);
|
||||
final Contact newData = contacts[0];
|
||||
|
||||
if (groupMethod == GroupMethod.CATEGORIES && newData.group) {
|
||||
groupMethod = GroupMethod.GROUP_VCARDS;
|
||||
App.log.warning("Received group VCard although group method is CATEGORIES. Deleting all groups; new group method: " + groupMethod);
|
||||
localAddressBook().removeGroups();
|
||||
settings.setGroupMethod(groupMethod);
|
||||
}
|
||||
|
||||
// update local contact, if it exists
|
||||
LocalResource local = localResources.get(fileName);
|
||||
if (local != null) {
|
||||
App.log.log(Level.INFO, "Updating " + fileName + " in local address book", newData);
|
||||
|
||||
if (local instanceof LocalGroup && newData.group) {
|
||||
// update group
|
||||
LocalGroup group = (LocalGroup)local;
|
||||
group.eTag = eTag;
|
||||
group.updateFromServer(newData);
|
||||
syncResult.stats.numUpdates++;
|
||||
|
||||
} else if (local instanceof LocalContact && !newData.group) {
|
||||
// update contact
|
||||
LocalContact contact = (LocalContact)local;
|
||||
contact.eTag = eTag;
|
||||
contact.update(newData);
|
||||
syncResult.stats.numUpdates++;
|
||||
|
||||
} else {
|
||||
App.log.info("Adding " + fileName + " to local address book");
|
||||
localContact = new LocalContact(localAddressBook(), newData, fileName, eTag);
|
||||
localContact.add();
|
||||
syncResult.stats.numInserts++;
|
||||
// group has become an individual contact or vice versa
|
||||
try {
|
||||
local.delete();
|
||||
local = null;
|
||||
} catch(CalendarStorageException e) {
|
||||
// CalendarStorageException is not used by LocalGroup and LocalContact
|
||||
}
|
||||
}
|
||||
} else
|
||||
App.log.severe("Received VCard with not exactly one VCARD, ignoring " + fileName);
|
||||
}
|
||||
|
||||
if (local == null) {
|
||||
if (newData.group) {
|
||||
App.log.log(Level.INFO, "Creating local group", newData);
|
||||
LocalGroup group = new LocalGroup(localAddressBook(), newData, fileName, eTag);
|
||||
group.create();
|
||||
|
||||
local = group;
|
||||
} else {
|
||||
App.log.log(Level.INFO, "Creating local contact", newData);
|
||||
LocalContact contact = new LocalContact(localAddressBook(), newData, fileName, eTag);
|
||||
contact.create();
|
||||
|
||||
local = contact;
|
||||
}
|
||||
syncResult.stats.numInserts++;
|
||||
}
|
||||
|
||||
if (groupMethod == GroupMethod.CATEGORIES && local instanceof LocalContact) {
|
||||
// VCard3: update group memberships from CATEGORIES
|
||||
LocalContact contact = (LocalContact)local;
|
||||
|
||||
BatchOperation batch = new BatchOperation(provider);
|
||||
contact.removeGroupMemberships(batch);
|
||||
|
||||
for (String category : contact.getContact().categories) {
|
||||
long groupID = localAddressBook().findOrCreateGroup(category);
|
||||
contact.addToGroup(batch, groupID);
|
||||
}
|
||||
|
||||
batch.commit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,68 +9,107 @@
|
||||
package at.bitfire.davdroid.syncadapter;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.AbstractThreadedSyncAdapter;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SyncResult;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
import at.bitfire.davdroid.AccountSettings;
|
||||
import at.bitfire.davdroid.App;
|
||||
import at.bitfire.davdroid.InvalidAccountException;
|
||||
import at.bitfire.davdroid.model.ServiceDB;
|
||||
import at.bitfire.davdroid.Constants;
|
||||
import at.bitfire.davdroid.R;
|
||||
import at.bitfire.davdroid.ui.PermissionsActivity;
|
||||
|
||||
public abstract class SyncAdapterService extends Service {
|
||||
|
||||
ServiceDB.OpenHelper dbHelper;
|
||||
AbstractThreadedSyncAdapter syncAdapter;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
dbHelper = new ServiceDB.OpenHelper(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
dbHelper.close();
|
||||
}
|
||||
abstract protected AbstractThreadedSyncAdapter syncAdapter();
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return syncAdapter.getSyncAdapterBinder();
|
||||
return syncAdapter().getSyncAdapterBinder();
|
||||
}
|
||||
|
||||
|
||||
public static abstract class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
protected final SQLiteDatabase db;
|
||||
|
||||
public SyncAdapter(Context context, ServiceDB.OpenHelper dbHelper) {
|
||||
public SyncAdapter(Context context) {
|
||||
super(context, false);
|
||||
db = dbHelper.getReadableDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
|
||||
App.log.info("Starting " + authority + " sync");
|
||||
App.log.info("Sync for " + authority + " has been initiated");
|
||||
|
||||
// required for dav4android (ServiceLoader)
|
||||
Thread.currentThread().setContextClassLoader(getContext().getClassLoader());
|
||||
|
||||
// peek into AccountSettings to cause possible migration (v0.9 -> v1.0)
|
||||
try {
|
||||
new AccountSettings(getContext(), account);
|
||||
} catch (InvalidAccountException e) {
|
||||
App.log.log(Level.SEVERE, "Couldn't check for updated account settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSecurityException(Account account, Bundle extras, String authority, SyncResult syncResult) {
|
||||
App.log.log(Level.WARNING, "Security exception when opening content provider for " + authority);
|
||||
syncResult.databaseError = true;
|
||||
|
||||
Intent intent = new Intent(getContext(), PermissionsActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
Notification notify = new NotificationCompat.Builder(getContext())
|
||||
.setSmallIcon(R.drawable.ic_error_light)
|
||||
.setLargeIcon(((BitmapDrawable)getContext().getResources().getDrawable(R.drawable.ic_launcher)).getBitmap())
|
||||
.setContentTitle(getContext().getString(R.string.sync_error_permissions))
|
||||
.setContentText(getContext().getString(R.string.sync_error_permissions_text))
|
||||
.setContentIntent(PendingIntent.getActivity(getContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT))
|
||||
.setCategory(NotificationCompat.CATEGORY_ERROR)
|
||||
.setLocalOnly(true)
|
||||
.build();
|
||||
NotificationManager nm = (NotificationManager)getContext().getSystemService(NOTIFICATION_SERVICE);
|
||||
nm.notify(Constants.NOTIFICATION_PERMISSIONS, notify);
|
||||
}
|
||||
|
||||
protected boolean checkSyncConditions(@NonNull AccountSettings settings) {
|
||||
if (settings.getSyncWifiOnly()) {
|
||||
ConnectivityManager cm = (ConnectivityManager)getContext().getSystemService(CONNECTIVITY_SERVICE);
|
||||
NetworkInfo network = cm.getActiveNetworkInfo();
|
||||
if (network == null) {
|
||||
App.log.info("No network available, stopping");
|
||||
return false;
|
||||
}
|
||||
if (network.getType() != ConnectivityManager.TYPE_WIFI || !network.isConnected()) {
|
||||
App.log.info("Not on connected WiFi, stopping");
|
||||
return false;
|
||||
}
|
||||
|
||||
String onlySSID = settings.getSyncWifiOnlySSID();
|
||||
if (onlySSID != null) {
|
||||
onlySSID = "\"" + onlySSID + "\"";
|
||||
WifiManager wifi = (WifiManager)getContext().getSystemService(WIFI_SERVICE);
|
||||
WifiInfo info = wifi.getConnectionInfo();
|
||||
if (info == null || !onlySSID.equals(info.getSSID())) {
|
||||
App.log.info("Connected to wrong WiFi network (" + info.getSSID() + ", required: " + onlySSID + "), ignoring");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@ package at.bitfire.davdroid.syncadapter;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SyncResult;
|
||||
import android.os.Build;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.NotificationCompat;
|
||||
import android.text.TextUtils;
|
||||
@@ -46,7 +46,6 @@ import at.bitfire.davdroid.InvalidAccountException;
|
||||
import at.bitfire.davdroid.R;
|
||||
import at.bitfire.davdroid.resource.LocalCollection;
|
||||
import at.bitfire.davdroid.resource.LocalResource;
|
||||
import at.bitfire.davdroid.ui.AccountActivity;
|
||||
import at.bitfire.davdroid.ui.AccountSettingsActivity;
|
||||
import at.bitfire.davdroid.ui.DebugInfoActivity;
|
||||
import at.bitfire.ical4android.CalendarStorageException;
|
||||
@@ -67,10 +66,11 @@ abstract public class SyncManager {
|
||||
SYNC_PHASE_LIST_REMOTE = 7,
|
||||
SYNC_PHASE_COMPARE_LOCAL_REMOTE = 8,
|
||||
SYNC_PHASE_DOWNLOAD_REMOTE = 9,
|
||||
SYNC_PHASE_SAVE_SYNC_STATE = 10;
|
||||
SYNC_PHASE_POST_PROCESSING = 10,
|
||||
SYNC_PHASE_SAVE_SYNC_STATE = 11;
|
||||
|
||||
protected final NotificationManager notificationManager;
|
||||
protected final int notificationId;
|
||||
protected final String uniqueCollectionId;
|
||||
|
||||
protected final Context context;
|
||||
protected final Account account;
|
||||
@@ -100,24 +100,24 @@ abstract public class SyncManager {
|
||||
|
||||
|
||||
|
||||
public SyncManager(int notificationId, Context context, Account account, Bundle extras, String authority, SyncResult syncResult) throws InvalidAccountException {
|
||||
public SyncManager(Context context, Account account, AccountSettings settings, Bundle extras, String authority, SyncResult syncResult, String uniqueCollectionId) throws InvalidAccountException {
|
||||
this.context = context;
|
||||
this.account = account;
|
||||
this.settings = settings;
|
||||
this.extras = extras;
|
||||
this.authority = authority;
|
||||
this.syncResult = syncResult;
|
||||
|
||||
// get account settings (for sync interval etc.)
|
||||
settings = new AccountSettings(context, account);
|
||||
|
||||
// create HttpClient with given logger
|
||||
httpClient = HttpClient.create(context, account);
|
||||
|
||||
// dismiss previous error notifications
|
||||
this.uniqueCollectionId = uniqueCollectionId;
|
||||
notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.cancel(account.name, this.notificationId = notificationId);
|
||||
notificationManager.cancel(uniqueCollectionId, notificationId());
|
||||
}
|
||||
|
||||
protected abstract int notificationId();
|
||||
protected abstract String getSyncErrorTitle();
|
||||
|
||||
@TargetApi(21)
|
||||
@@ -170,6 +170,10 @@ abstract public class SyncManager {
|
||||
App.log.info("Downloading remote entries");
|
||||
downloadRemote();
|
||||
|
||||
syncPhase = SYNC_PHASE_POST_PROCESSING;
|
||||
App.log.info("Post-processing");
|
||||
postProcess();
|
||||
|
||||
syncPhase = SYNC_PHASE_SAVE_SYNC_STATE;
|
||||
App.log.info("Saving sync state");
|
||||
saveSyncState();
|
||||
@@ -215,16 +219,20 @@ abstract public class SyncManager {
|
||||
detailsIntent.putExtra(AccountSettingsActivity.EXTRA_ACCOUNT, account);
|
||||
} else {
|
||||
detailsIntent = new Intent(context, DebugInfoActivity.class);
|
||||
detailsIntent.putExtra(DebugInfoActivity.KEY_EXCEPTION, e);
|
||||
detailsIntent.putExtra(DebugInfoActivity.KEY_THROWABLE, e);
|
||||
detailsIntent.putExtra(DebugInfoActivity.KEY_ACCOUNT, account);
|
||||
detailsIntent.putExtra(DebugInfoActivity.KEY_AUTHORITY, authority);
|
||||
detailsIntent.putExtra(DebugInfoActivity.KEY_PHASE, syncPhase);
|
||||
}
|
||||
|
||||
// to make the PendingIntent unique
|
||||
detailsIntent.setData(Uri.parse("uri://" + getClass().getName() + "/" + uniqueCollectionId));
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
|
||||
builder .setSmallIcon(R.drawable.ic_launcher)
|
||||
builder .setSmallIcon(R.drawable.ic_error_light)
|
||||
.setLargeIcon(((BitmapDrawable)context.getResources().getDrawable(R.drawable.ic_launcher)).getBitmap())
|
||||
.setContentTitle(getSyncErrorTitle())
|
||||
.setContentIntent(PendingIntent.getActivity(context, notificationId, detailsIntent, PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
.setContentIntent(PendingIntent.getActivity(context, 0, detailsIntent, PendingIntent.FLAG_CANCEL_CURRENT))
|
||||
.setCategory(NotificationCompat.CATEGORY_ERROR)
|
||||
.setLocalOnly(true);
|
||||
|
||||
@@ -236,7 +244,7 @@ abstract public class SyncManager {
|
||||
// should never happen
|
||||
}
|
||||
|
||||
notificationManager.notify(account.name, notificationId, builder.build());
|
||||
notificationManager.notify(uniqueCollectionId, notificationId(), builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,9 +283,10 @@ abstract public class SyncManager {
|
||||
|
||||
protected void prepareDirty() throws CalendarStorageException, ContactsStorageException {
|
||||
// assign file names and UIDs to new contacts so that we can use the file name as an index
|
||||
App.log.info("Looking for contacts/groups without file name");
|
||||
for (LocalResource local : localCollection.getWithoutFileName()) {
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
App.log.info("Found local record #" + local.getId() + " without file name; assigning file name/UID based on " + uuid);
|
||||
App.log.fine("Found local record #" + local.getId() + " without file name; assigning file name/UID based on " + uuid);
|
||||
local.updateFileNameAndUID(uuid);
|
||||
}
|
||||
}
|
||||
@@ -302,7 +311,6 @@ abstract public class SyncManager {
|
||||
RequestBody body = prepareUpload(local);
|
||||
|
||||
try {
|
||||
|
||||
if (local.getETag() == null) {
|
||||
App.log.info("Uploading new record " + fileName);
|
||||
remote.put(body, null, true);
|
||||
@@ -310,7 +318,6 @@ abstract public class SyncManager {
|
||||
App.log.info("Uploading locally modified record " + fileName);
|
||||
remote.put(body, local.getETag(), false);
|
||||
}
|
||||
|
||||
} catch (ConflictException|PreconditionFailedException e) {
|
||||
// we can't interact with the user to resolve the conflict, so we treat 409 like 412
|
||||
App.log.log(Level.INFO, "Resource has been modified on the server before upload, ignoring", e);
|
||||
@@ -424,6 +431,12 @@ abstract public class SyncManager {
|
||||
*/
|
||||
abstract protected void downloadRemote() throws IOException, HttpException, DavException, ContactsStorageException, CalendarStorageException;
|
||||
|
||||
/**
|
||||
* For post-processing of entries, for instance assigning groups.
|
||||
*/
|
||||
protected void postProcess() throws CalendarStorageException, ContactsStorageException {
|
||||
}
|
||||
|
||||
protected void saveSyncState() throws CalendarStorageException, ContactsStorageException {
|
||||
/* Save sync state (CTag). It doesn't matter if it has changed during the sync process
|
||||
(for instance, because another client has uploaded changes), because this will simply
|
||||
|
||||
@@ -8,49 +8,53 @@
|
||||
package at.bitfire.davdroid.syncadapter;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.app.Service;
|
||||
import android.content.AbstractThreadedSyncAdapter;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SyncResult;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.dmfs.provider.tasks.TaskContract;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import at.bitfire.davdroid.AccountSettings;
|
||||
import at.bitfire.davdroid.App;
|
||||
import at.bitfire.davdroid.InvalidAccountException;
|
||||
import at.bitfire.davdroid.model.CollectionInfo;
|
||||
import at.bitfire.davdroid.model.ServiceDB;
|
||||
import at.bitfire.davdroid.model.ServiceDB.Collections;
|
||||
import at.bitfire.davdroid.model.ServiceDB.OpenHelper;
|
||||
import at.bitfire.davdroid.model.ServiceDB.Services;
|
||||
import at.bitfire.davdroid.resource.LocalTaskList;
|
||||
import at.bitfire.ical4android.CalendarStorageException;
|
||||
import at.bitfire.ical4android.TaskProvider;
|
||||
import lombok.Cleanup;
|
||||
|
||||
/**
|
||||
* Synchronization manager for CalDAV collections; handles tasks ({@code VTODO}).
|
||||
*/
|
||||
public class TasksSyncAdapterService extends SyncAdapterService {
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
syncAdapter = new SyncAdapter(this, dbHelper);
|
||||
}
|
||||
@Override
|
||||
protected AbstractThreadedSyncAdapter syncAdapter() {
|
||||
return new SyncAdapter(this);
|
||||
}
|
||||
|
||||
|
||||
private static class SyncAdapter extends SyncAdapterService.SyncAdapter {
|
||||
|
||||
public SyncAdapter(Context context, OpenHelper dbHelper) {
|
||||
super(context, dbHelper);
|
||||
public SyncAdapter(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,56 +66,66 @@ public class TasksSyncAdapterService extends SyncAdapterService {
|
||||
if (provider == null)
|
||||
throw new CalendarStorageException("Couldn't access OpenTasks provider");
|
||||
|
||||
updateLocalTaskLists(provider, account);
|
||||
AccountSettings settings = new AccountSettings(getContext(), account);
|
||||
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(settings))
|
||||
return;
|
||||
|
||||
for (LocalTaskList taskList : (LocalTaskList[])LocalTaskList.find(account, provider, LocalTaskList.Factory.INSTANCE, null, null)) {
|
||||
App.log.info("Synchronizing task list #" + taskList.getId() + ", URL: " + taskList.getSyncId());
|
||||
TasksSyncManager syncManager = new TasksSyncManager(getContext(), account, extras, authority, provider, syncResult, taskList);
|
||||
updateLocalTaskLists(provider, account, settings);
|
||||
|
||||
for (LocalTaskList taskList : (LocalTaskList[])LocalTaskList.find(account, provider, LocalTaskList.Factory.INSTANCE, TaskContract.TaskLists.SYNC_ENABLED + "!=0", null)) {
|
||||
App.log.info("Synchronizing task list #" + taskList.getId() + " [" + taskList.getSyncId() + "]");
|
||||
TasksSyncManager syncManager = new TasksSyncManager(getContext(), account, settings, extras, authority, provider, syncResult, taskList);
|
||||
syncManager.performSync();
|
||||
}
|
||||
} catch (CalendarStorageException e) {
|
||||
App.log.log(Level.SEVERE, "Couldn't enumerate local task lists", e);
|
||||
} catch (InvalidAccountException e) {
|
||||
App.log.log(Level.SEVERE, "Couldn't get account settings", e);
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
|
||||
App.log.info("Task sync complete");
|
||||
}
|
||||
|
||||
private void updateLocalTaskLists(TaskProvider provider, Account account) throws CalendarStorageException {
|
||||
// enumerate remote and local task lists
|
||||
Long service = getService(account);
|
||||
Map<String, CollectionInfo> remote = remoteTaskLists(service);
|
||||
LocalTaskList[] local = (LocalTaskList[])LocalTaskList.find(account, provider, LocalTaskList.Factory.INSTANCE, null, null);
|
||||
private void updateLocalTaskLists(TaskProvider provider, Account account, AccountSettings settings) throws CalendarStorageException {
|
||||
SQLiteOpenHelper dbHelper = new ServiceDB.OpenHelper(getContext());
|
||||
try {
|
||||
// enumerate remote and local task lists
|
||||
SQLiteDatabase db = dbHelper.getReadableDatabase();
|
||||
Long service = getService(db, account);
|
||||
Map<String, CollectionInfo> remote = remoteTaskLists(db, service);
|
||||
LocalTaskList[] local = (LocalTaskList[])LocalTaskList.find(account, provider, LocalTaskList.Factory.INSTANCE, null, null);
|
||||
|
||||
// delete obsolete local task lists
|
||||
for (LocalTaskList list : local) {
|
||||
String url = list.getSyncId();
|
||||
if (!remote.containsKey(url)) {
|
||||
App.log.fine("Deleting obsolete local task list" + url);
|
||||
list.delete();
|
||||
} else {
|
||||
// remote CollectionInfo found for this local collection, update data
|
||||
CollectionInfo info = remote.get(url);
|
||||
App.log.fine("Updating local task list " + url + " with " + info);
|
||||
list.update(info);
|
||||
// we already have a local task list for this remote collection, don't take into consideration anymore
|
||||
remote.remove(url);
|
||||
boolean updateColors = settings.getManageCalendarColors();
|
||||
|
||||
// delete obsolete local task lists
|
||||
for (LocalTaskList list : local) {
|
||||
String url = list.getSyncId();
|
||||
if (!remote.containsKey(url)) {
|
||||
App.log.fine("Deleting obsolete local task list" + url);
|
||||
list.delete();
|
||||
} else {
|
||||
// remote CollectionInfo found for this local collection, update data
|
||||
CollectionInfo info = remote.get(url);
|
||||
App.log.fine("Updating local task list " + url + " with " + info);
|
||||
list.update(info, updateColors);
|
||||
// we already have a local task list for this remote collection, don't take into consideration anymore
|
||||
remote.remove(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create new local task lists
|
||||
for (String url : remote.keySet()) {
|
||||
CollectionInfo info = remote.get(url);
|
||||
App.log.info("Adding local task list " + info);
|
||||
LocalTaskList.create(account, provider, info);
|
||||
// create new local task lists
|
||||
for (String url : remote.keySet()) {
|
||||
CollectionInfo info = remote.get(url);
|
||||
App.log.info("Adding local task list " + info);
|
||||
LocalTaskList.create(account, provider, info);
|
||||
}
|
||||
} finally {
|
||||
dbHelper.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Long getService(Account account) {
|
||||
Long getService(@NonNull SQLiteDatabase db, @NonNull Account account) {
|
||||
@Cleanup Cursor c = db.query(Services._TABLE, new String[] { Services.ID },
|
||||
Services.ACCOUNT_NAME + "=? AND " + Services.SERVICE + "=?", new String[] { account.name, Services.SERVICE_CALDAV }, null, null, null);
|
||||
if (c.moveToNext())
|
||||
@@ -121,7 +135,7 @@ public class TasksSyncAdapterService extends SyncAdapterService {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Map<String, CollectionInfo> remoteTaskLists(Long service) {
|
||||
private Map<String, CollectionInfo> remoteTaskLists(@NonNull SQLiteDatabase db, Long service) {
|
||||
Map<String, CollectionInfo> collections = new LinkedHashMap<>();
|
||||
if (service != null) {
|
||||
@Cleanup Cursor cursor = db.query(Collections._TABLE, null,
|
||||
|
||||
@@ -9,22 +9,18 @@
|
||||
package at.bitfire.davdroid.syncadapter;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.SyncResult;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.apache.commons.codec.Charsets;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dmfs.provider.tasks.TaskContract.TaskLists;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -34,12 +30,11 @@ import at.bitfire.dav4android.DavCalendar;
|
||||
import at.bitfire.dav4android.DavResource;
|
||||
import at.bitfire.dav4android.exception.DavException;
|
||||
import at.bitfire.dav4android.exception.HttpException;
|
||||
import at.bitfire.dav4android.property.CalendarColor;
|
||||
import at.bitfire.dav4android.property.CalendarData;
|
||||
import at.bitfire.dav4android.property.DisplayName;
|
||||
import at.bitfire.dav4android.property.GetCTag;
|
||||
import at.bitfire.dav4android.property.GetContentType;
|
||||
import at.bitfire.dav4android.property.GetETag;
|
||||
import at.bitfire.davdroid.AccountSettings;
|
||||
import at.bitfire.davdroid.App;
|
||||
import at.bitfire.davdroid.ArrayUtils;
|
||||
import at.bitfire.davdroid.Constants;
|
||||
@@ -65,12 +60,17 @@ public class TasksSyncManager extends SyncManager {
|
||||
final protected TaskProvider provider;
|
||||
|
||||
|
||||
public TasksSyncManager(Context context, Account account, Bundle extras, String authority, TaskProvider provider, SyncResult result, LocalTaskList taskList) throws InvalidAccountException {
|
||||
super(Constants.NOTIFICATION_TASK_SYNC, context, account, extras, authority, result);
|
||||
public TasksSyncManager(Context context, Account account, AccountSettings settings, Bundle extras, String authority, TaskProvider provider, SyncResult result, LocalTaskList taskList) throws InvalidAccountException {
|
||||
super(context, account, settings, extras, authority, result, "taskList/" + taskList.getId());
|
||||
this.provider = provider;
|
||||
localCollection = taskList;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int notificationId() {
|
||||
return Constants.NOTIFICATION_TASK_SYNC;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSyncErrorTitle() {
|
||||
return context.getString(R.string.sync_error_tasks, account.name);
|
||||
@@ -91,9 +91,14 @@ public class TasksSyncManager extends SyncManager {
|
||||
@Override
|
||||
protected RequestBody prepareUpload(LocalResource resource) throws IOException, CalendarStorageException {
|
||||
LocalTask local = (LocalTask)resource;
|
||||
App.log.log(Level.FINE, "Preparing upload of task " + local.getFileName(), local.getTask() );
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
local.getTask().write(os);
|
||||
|
||||
return RequestBody.create(
|
||||
DavCalendar.MIME_ICALENDAR,
|
||||
local.getTask().toStream().toByteArray()
|
||||
os.toByteArray()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -125,7 +130,11 @@ public class TasksSyncManager extends SyncManager {
|
||||
DavResource remote = bunch[0];
|
||||
|
||||
ResponseBody body = remote.get("text/calendar");
|
||||
String eTag = ((GetETag)remote.properties.get(GetETag.NAME)).eTag;
|
||||
|
||||
// CalDAV servers MUST return ETag on GET [https://tools.ietf.org/html/rfc4791#section-5.3.4]
|
||||
GetETag eTag = (GetETag)remote.properties.get(GetETag.NAME);
|
||||
if (eTag == null || StringUtils.isEmpty(eTag.eTag))
|
||||
throw new DavException("Received CalDAV GET response without ETag for " + remote.location);
|
||||
|
||||
Charset charset = Charsets.UTF_8;
|
||||
MediaType contentType = body.contentType();
|
||||
@@ -133,7 +142,7 @@ public class TasksSyncManager extends SyncManager {
|
||||
charset = contentType.charset(Charsets.UTF_8);
|
||||
|
||||
@Cleanup InputStream stream = body.byteStream();
|
||||
processVTodo(remote.fileName(), eTag, stream, charset);
|
||||
processVTodo(remote.fileName(), eTag.eTag, stream, charset);
|
||||
|
||||
} else {
|
||||
// multiple contacts, use multi-get
|
||||
|
||||
@@ -64,7 +64,7 @@ public class AboutActivity extends AppCompatActivity {
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class ComponentInfo {
|
||||
final String title, version, website, copyright;
|
||||
final String title, version, website, copyright;
|
||||
final int licenseInfo;
|
||||
final String licenseTextFile;
|
||||
}
|
||||
@@ -72,32 +72,32 @@ public class AboutActivity extends AppCompatActivity {
|
||||
private final static ComponentInfo components[] = {
|
||||
new ComponentInfo(
|
||||
"DAVdroid", BuildConfig.VERSION_NAME, "https://davdroid.bitfire.at",
|
||||
"© " + DateFormatUtils.format(BuildConfig.buildTime, "yyyy") + " Ricki Hirner, Bernhard Stockmann (bitfire web engineering)",
|
||||
DateFormatUtils.format(BuildConfig.buildTime, "yyyy") + " Ricki Hirner, Bernhard Stockmann (bitfire web engineering)",
|
||||
R.string.about_license_info_no_warranty, "gpl-3.0-standalone.html"
|
||||
), new ComponentInfo(
|
||||
"AmbilWarna", null, "https://github.com/yukuku/ambilwarna",
|
||||
"© Yuku", R.string.about_license_info_no_warranty, "apache2.html"
|
||||
"Yuku", R.string.about_license_info_no_warranty, "apache2.html"
|
||||
), new ComponentInfo(
|
||||
"Apache Commons", null, "http://commons.apache.org/",
|
||||
"Apache Software Foundation", R.string.about_license_info_no_warranty, "apache2.html"
|
||||
), new ComponentInfo(
|
||||
"dnsjava", null, "http://dnsjava.org/",
|
||||
"© Brian Wellington", R.string.about_license_info_no_warranty, "bsd.html"
|
||||
"Brian Wellington", R.string.about_license_info_no_warranty, "bsd.html"
|
||||
), new ComponentInfo(
|
||||
"ez-vcard", Ezvcard.VERSION, "https://github.com/mangstadt/ez-vcard",
|
||||
"© Michael Angstadt", R.string.about_license_info_no_warranty, "bsd.html"
|
||||
"Michael Angstadt", R.string.about_license_info_no_warranty, "bsd.html"
|
||||
), new ComponentInfo(
|
||||
"ical4j", "2.x", "http://ical4j.github.io/",
|
||||
"© Ben Fortuna", R.string.about_license_info_no_warranty, "bsd-3clause.html"
|
||||
"ical4j", "2.x", "https://ical4j.github.io/",
|
||||
"Ben Fortuna", R.string.about_license_info_no_warranty, "bsd-3clause.html"
|
||||
), new ComponentInfo(
|
||||
"MemorizingTrustManager", null, "https://github.com/ge0rg/MemorizingTrustManager",
|
||||
"© Georg Lukas", R.string.about_license_info_no_warranty, "mit.html"
|
||||
"Georg Lukas", R.string.about_license_info_no_warranty, "mit.html"
|
||||
), new ComponentInfo(
|
||||
"OkHttp", null, "https://square.github.io/okhttp/",
|
||||
"© Square, Inc.", R.string.about_license_info_no_warranty, "apache2.html"
|
||||
"Square, Inc.", R.string.about_license_info_no_warranty, "apache2.html"
|
||||
), new ComponentInfo(
|
||||
"Project Lombok", null, "https://projectlombok.org/",
|
||||
"© The Project Lombok Authors", R.string.about_license_info_no_warranty, "mit.html"
|
||||
"The Project Lombok Authors", R.string.about_license_info_no_warranty, "mit.html"
|
||||
)
|
||||
};
|
||||
|
||||
@@ -152,7 +152,7 @@ public class AboutActivity extends AppCompatActivity {
|
||||
tv.setText(info.website);
|
||||
|
||||
tv = (TextView)v.findViewById(R.id.copyright);
|
||||
tv.setText(info.copyright);
|
||||
tv.setText("© " + info.copyright);
|
||||
|
||||
tv = (TextView)v.findViewById(R.id.license_info);
|
||||
tv.setText(info.licenseInfo);
|
||||
|
||||
@@ -62,11 +62,8 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import at.bitfire.davdroid.AccountSettings;
|
||||
import at.bitfire.davdroid.App;
|
||||
import at.bitfire.davdroid.Constants;
|
||||
import at.bitfire.davdroid.DavService;
|
||||
import at.bitfire.davdroid.InvalidAccountException;
|
||||
import at.bitfire.davdroid.R;
|
||||
import at.bitfire.davdroid.model.CollectionInfo;
|
||||
import at.bitfire.davdroid.model.ServiceDB;
|
||||
@@ -152,12 +149,15 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
Intent intent;
|
||||
switch (item.getItemId()) {
|
||||
case R.id.refresh_address_books:
|
||||
Intent intent = new Intent(this, DavService.class);
|
||||
intent.setAction(DavService.ACTION_REFRESH_COLLECTIONS);
|
||||
intent.putExtra(DavService.EXTRA_DAV_SERVICE_ID, accountInfo.carddav.id);
|
||||
startService(intent);
|
||||
if (accountInfo.carddav != null) {
|
||||
intent = new Intent(this, DavService.class);
|
||||
intent.setAction(DavService.ACTION_REFRESH_COLLECTIONS);
|
||||
intent.putExtra(DavService.EXTRA_DAV_SERVICE_ID, accountInfo.carddav.id);
|
||||
startService(intent);
|
||||
}
|
||||
break;
|
||||
case R.id.create_address_book:
|
||||
intent = new Intent(this, CreateAddressBookActivity.class);
|
||||
@@ -165,10 +165,12 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
|
||||
startActivity(intent);
|
||||
break;
|
||||
case R.id.refresh_calendars:
|
||||
intent = new Intent(this, DavService.class);
|
||||
intent.setAction(DavService.ACTION_REFRESH_COLLECTIONS);
|
||||
intent.putExtra(DavService.EXTRA_DAV_SERVICE_ID, accountInfo.caldav.id);
|
||||
startService(intent);
|
||||
if (accountInfo.caldav != null) {
|
||||
intent = new Intent(this, DavService.class);
|
||||
intent.setAction(DavService.ACTION_REFRESH_COLLECTIONS);
|
||||
intent.putExtra(DavService.EXTRA_DAV_SERVICE_ID, accountInfo.caldav.id);
|
||||
startService(intent);
|
||||
}
|
||||
break;
|
||||
case R.id.create_calendar:
|
||||
intent = new Intent(this, CreateCalendarActivity.class);
|
||||
@@ -197,7 +199,7 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
|
||||
OpenHelper dbHelper = new OpenHelper(AccountActivity.this);
|
||||
try {
|
||||
SQLiteDatabase db = dbHelper.getWritableDatabase();
|
||||
db.beginTransaction();
|
||||
db.beginTransactionNonExclusive();
|
||||
|
||||
if (list.getChoiceMode() == AbsListView.CHOICE_MODE_SINGLE) {
|
||||
// disable all other collections
|
||||
@@ -373,15 +375,6 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
|
||||
|
||||
@Override
|
||||
public AccountInfo loadInBackground() {
|
||||
// peek into AccountSettings to call possible 0.9 -> 1.0 migration
|
||||
// The next line can be removed as soon as migration from 0.9 is not required anymore!
|
||||
try {
|
||||
new AccountSettings(getContext(), new Account(accountName, Constants.ACCOUNT_TYPE));
|
||||
} catch (InvalidAccountException e) {
|
||||
App.log.log(Level.INFO, "Account doesn't exist (anymore)", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
AccountInfo info = new AccountInfo();
|
||||
try {
|
||||
SQLiteDatabase db = dbHelper.getReadableDatabase();
|
||||
@@ -391,6 +384,11 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
|
||||
new String[] { Services.ID, Services.SERVICE },
|
||||
Services.ACCOUNT_NAME + "=?", new String[] { accountName },
|
||||
null, null, null);
|
||||
|
||||
if (cursor.getCount() == 0)
|
||||
// no services, account not useable
|
||||
return null;
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
long id = cursor.getLong(0);
|
||||
String service = cursor.getString(1);
|
||||
@@ -557,7 +555,8 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
|
||||
|
||||
for (String authority : authorities) {
|
||||
Bundle extras = new Bundle();
|
||||
extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
|
||||
extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); // manual sync
|
||||
extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); // run immediately (don't queue)
|
||||
ContentResolver.requestSync(account, authority, extras);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,19 +9,18 @@
|
||||
package at.bitfire.davdroid.ui;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.provider.CalendarContract;
|
||||
import android.provider.ContactsContract;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.preference.EditTextPreference;
|
||||
import android.support.v7.preference.ListPreference;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.PreferenceFragmentCompat;
|
||||
import android.support.v7.preference.SwitchPreferenceCompat;
|
||||
import android.text.TextUtils;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import java.util.logging.Level;
|
||||
@@ -31,6 +30,7 @@ import at.bitfire.davdroid.App;
|
||||
import at.bitfire.davdroid.InvalidAccountException;
|
||||
import at.bitfire.davdroid.R;
|
||||
import at.bitfire.ical4android.TaskProvider;
|
||||
import at.bitfire.vcard4android.GroupMethod;
|
||||
|
||||
public class AccountSettingsActivity extends AppCompatActivity {
|
||||
public final static String EXTRA_ACCOUNT = "account";
|
||||
@@ -85,7 +85,7 @@ public class AccountSettingsActivity extends AppCompatActivity {
|
||||
|
||||
try {
|
||||
settings = new AccountSettings(getActivity(), account);
|
||||
} catch(InvalidAccountException e) {
|
||||
} catch(InvalidAccountException e) {
|
||||
App.log.log(Level.INFO, "Account is invalid or doesn't exist (anymore)", e);
|
||||
getActivity().finish();
|
||||
return;
|
||||
@@ -98,8 +98,9 @@ public class AccountSettingsActivity extends AppCompatActivity {
|
||||
prefUserName.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
settings.username((String) newValue);
|
||||
refresh(); return false;
|
||||
settings.username((String)newValue);
|
||||
refresh();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -107,8 +108,9 @@ public class AccountSettingsActivity extends AppCompatActivity {
|
||||
prefPassword.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
settings.password((String) newValue);
|
||||
refresh(); return false;
|
||||
settings.password((String)newValue);
|
||||
refresh();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -117,8 +119,9 @@ public class AccountSettingsActivity extends AppCompatActivity {
|
||||
prefPreemptive.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
settings.preemptiveAuth((Boolean) newValue);
|
||||
refresh(); return false;
|
||||
settings.preemptiveAuth((Boolean)newValue);
|
||||
refresh();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -134,8 +137,9 @@ public class AccountSettingsActivity extends AppCompatActivity {
|
||||
prefSyncContacts.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
settings.setSyncInterval(ContactsContract.AUTHORITY, Long.parseLong((String) newValue));
|
||||
refresh(); return false;
|
||||
settings.setSyncInterval(ContactsContract.AUTHORITY, Long.parseLong((String)newValue));
|
||||
refresh();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -154,8 +158,9 @@ public class AccountSettingsActivity extends AppCompatActivity {
|
||||
prefSyncCalendars.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
settings.setSyncInterval(CalendarContract.AUTHORITY, Long.parseLong((String) newValue));
|
||||
refresh(); return false;
|
||||
settings.setSyncInterval(CalendarContract.AUTHORITY, Long.parseLong((String)newValue));
|
||||
refresh();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -174,8 +179,9 @@ public class AccountSettingsActivity extends AppCompatActivity {
|
||||
prefSyncTasks.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
settings.setSyncInterval(TaskProvider.ProviderName.OpenTasks.authority, Long.parseLong((String) newValue));
|
||||
refresh(); return false;
|
||||
settings.setSyncInterval(TaskProvider.ProviderName.OpenTasks.authority, Long.parseLong((String)newValue));
|
||||
refresh();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -183,30 +189,108 @@ public class AccountSettingsActivity extends AppCompatActivity {
|
||||
prefSyncTasks.setSummary(R.string.settings_sync_summary_not_available);
|
||||
}
|
||||
|
||||
final EditTextPreference prefTimeRangePastDays = (EditTextPreference)findPreference("caldav_time_range_past_days");
|
||||
Integer pastDays = settings.getTimeRangePastDays();
|
||||
if (pastDays != null) {
|
||||
prefTimeRangePastDays.setText(pastDays.toString());
|
||||
prefTimeRangePastDays.setSummary(getResources().getQuantityString(R.plurals.settings_sync_time_range_past_days, pastDays, pastDays));
|
||||
} else {
|
||||
prefTimeRangePastDays.setText(null);
|
||||
prefTimeRangePastDays.setSummary(R.string.settings_sync_time_range_past_none);
|
||||
}
|
||||
prefTimeRangePastDays.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
final SwitchPreferenceCompat prefWifiOnly = (SwitchPreferenceCompat)findPreference("sync_wifi_only");
|
||||
prefWifiOnly.setChecked(settings.getSyncWifiOnly());
|
||||
prefWifiOnly.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
int days;
|
||||
try {
|
||||
days = Integer.parseInt((String)newValue);
|
||||
} catch(NumberFormatException ignored) {
|
||||
days = -1;
|
||||
}
|
||||
settings.setTimeRangePastDays(days < 0 ? null : days);
|
||||
refresh(); return false;
|
||||
public boolean onPreferenceChange(Preference preference, Object wifiOnly) {
|
||||
settings.setSyncWiFiOnly((Boolean)wifiOnly);
|
||||
refresh();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
final EditTextPreference prefWifiOnlySSID = (EditTextPreference)findPreference("sync_wifi_only_ssid");
|
||||
final String onlySSID = settings.getSyncWifiOnlySSID();
|
||||
prefWifiOnlySSID.setText(onlySSID);
|
||||
if (onlySSID != null)
|
||||
prefWifiOnlySSID.setSummary(getString(R.string.settings_sync_wifi_only_ssid_on, onlySSID));
|
||||
else
|
||||
prefWifiOnlySSID.setSummary(R.string.settings_sync_wifi_only_ssid_off);
|
||||
prefWifiOnlySSID.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
String ssid = (String)newValue;
|
||||
settings.setSyncWifiOnlySSID(!TextUtils.isEmpty(ssid) ? ssid : null);
|
||||
refresh();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// category: CardDAV
|
||||
final SwitchPreferenceCompat prefRFC6868 = (SwitchPreferenceCompat)findPreference("vcard_rfc6868");
|
||||
if (syncIntervalContacts != null) {
|
||||
prefRFC6868.setChecked(settings.getVCardRFC6868());
|
||||
prefRFC6868.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object o) {
|
||||
settings.setVCardRFC6868((Boolean)o);
|
||||
refresh();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else
|
||||
prefRFC6868.setEnabled(false);
|
||||
|
||||
final ListPreference prefGroupMethod = (ListPreference)findPreference("contact_group_method");
|
||||
if (syncIntervalContacts != null) {
|
||||
prefGroupMethod.setValue(settings.getGroupMethod().name());
|
||||
prefGroupMethod.setSummary(prefGroupMethod.getEntry());
|
||||
prefGroupMethod.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object o) {
|
||||
String name = (String)o;
|
||||
settings.setGroupMethod(GroupMethod.valueOf(name));
|
||||
refresh();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else
|
||||
prefGroupMethod.setEnabled(false);
|
||||
|
||||
// category: CalDAV
|
||||
final EditTextPreference prefTimeRangePastDays = (EditTextPreference)findPreference("time_range_past_days");
|
||||
if (syncIntervalCalendars != null) {
|
||||
Integer pastDays = settings.getTimeRangePastDays();
|
||||
if (pastDays != null) {
|
||||
prefTimeRangePastDays.setText(pastDays.toString());
|
||||
prefTimeRangePastDays.setSummary(getResources().getQuantityString(R.plurals.settings_sync_time_range_past_days, pastDays, pastDays));
|
||||
} else {
|
||||
prefTimeRangePastDays.setText(null);
|
||||
prefTimeRangePastDays.setSummary(R.string.settings_sync_time_range_past_none);
|
||||
}
|
||||
prefTimeRangePastDays.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
int days;
|
||||
try {
|
||||
days = Integer.parseInt((String)newValue);
|
||||
} catch(NumberFormatException ignored) {
|
||||
days = -1;
|
||||
}
|
||||
settings.setTimeRangePastDays(days < 0 ? null : days);
|
||||
refresh();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else
|
||||
prefTimeRangePastDays.setEnabled(false);
|
||||
|
||||
final SwitchPreferenceCompat prefManageColors = (SwitchPreferenceCompat)findPreference("manage_calendar_colors");
|
||||
if (syncIntervalCalendars != null || syncIntervalTasks != null) {
|
||||
prefManageColors.setChecked(settings.getManageCalendarColors());
|
||||
prefManageColors.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
settings.setManageCalendarColors((Boolean)newValue);
|
||||
refresh();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else
|
||||
prefManageColors.setEnabled(false);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ package at.bitfire.davdroid.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.design.widget.NavigationView;
|
||||
@@ -22,6 +23,8 @@ import android.support.v7.widget.Toolbar;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import at.bitfire.davdroid.App;
|
||||
import at.bitfire.davdroid.BuildConfig;
|
||||
import at.bitfire.davdroid.Constants;
|
||||
import at.bitfire.davdroid.R;
|
||||
import at.bitfire.davdroid.ui.setup.LoginActivity;
|
||||
@@ -93,7 +96,8 @@ public class AccountsActivity extends AppCompatActivity implements NavigationVie
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Constants.webUri.buildUpon().appendEncodedPath("forums/").build()));
|
||||
break;
|
||||
case R.id.nav_donate:
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Constants.webUri.buildUpon().appendEncodedPath("donate/").build()));
|
||||
if (BuildConfig.FLAVOR != App.FLAVOR_GOOGLE_PLAY)
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Constants.webUri.buildUpon().appendEncodedPath("donate/").build()));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,19 +17,13 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.Loader;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.CalendarContract;
|
||||
import android.provider.ContactsContract;
|
||||
import android.support.v4.database.DatabaseUtilsCompat;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.TextView;
|
||||
@@ -55,7 +49,7 @@ import lombok.Cleanup;
|
||||
|
||||
public class DebugInfoActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<String> {
|
||||
public static final String
|
||||
KEY_EXCEPTION = "exception",
|
||||
KEY_THROWABLE = "throwable",
|
||||
KEY_LOGS = "logs",
|
||||
KEY_ACCOUNT = "account",
|
||||
KEY_AUTHORITY = "authority",
|
||||
@@ -151,14 +145,14 @@ public class DebugInfoActivity extends AppCompatActivity implements LoaderManage
|
||||
|
||||
@Override
|
||||
public String loadInBackground() {
|
||||
Exception exception = null;
|
||||
Throwable throwable = null;
|
||||
String logs = null,
|
||||
authority = null;
|
||||
Account account = null;
|
||||
int phase = -1;
|
||||
|
||||
if (extras != null) {
|
||||
exception = (Exception)extras.getSerializable(KEY_EXCEPTION);
|
||||
throwable = (Throwable)extras.getSerializable(KEY_THROWABLE);
|
||||
logs = extras.getString(KEY_LOGS);
|
||||
account = extras.getParcelable(KEY_ACCOUNT);
|
||||
authority = extras.getString(KEY_AUTHORITY);
|
||||
@@ -176,17 +170,17 @@ public class DebugInfoActivity extends AppCompatActivity implements LoaderManage
|
||||
if (authority != null)
|
||||
report.append("Authority: ").append(authority).append("\n");
|
||||
|
||||
if (exception instanceof HttpException) {
|
||||
HttpException http = (HttpException)exception;
|
||||
if (throwable instanceof HttpException) {
|
||||
HttpException http = (HttpException)throwable;
|
||||
if (http.request != null)
|
||||
report.append("\nHTTP REQUEST:\n").append(http.request).append("\n\n");
|
||||
if (http.response != null)
|
||||
report.append("HTTP RESPONSE:\n").append(http.response).append("\n");
|
||||
}
|
||||
|
||||
if (exception != null)
|
||||
if (throwable != null)
|
||||
report .append("\nEXCEPTION:\n")
|
||||
.append(ExceptionUtils.getStackTrace(exception));
|
||||
.append(ExceptionUtils.getStackTrace(throwable));
|
||||
|
||||
if (logs != null)
|
||||
report.append("\nLOGS:\n").append(logs).append("\n");
|
||||
@@ -215,11 +209,19 @@ public class DebugInfoActivity extends AppCompatActivity implements LoaderManage
|
||||
for (Account acct : accountManager.getAccountsByType(Constants.ACCOUNT_TYPE))
|
||||
try {
|
||||
AccountSettings settings = new AccountSettings(getContext(), acct);
|
||||
report.append(
|
||||
"Account: ").append(acct.name).append("\n" +
|
||||
" Address book sync. interval: ").append(syncStatus(settings, ContactsContract.AUTHORITY)).append("\n" +
|
||||
" Calendar sync. interval: ").append(syncStatus(settings, CalendarContract.AUTHORITY)).append("\n" +
|
||||
" OpenTasks sync. interval: ").append(syncStatus(settings, "org.dmfs.tasks")).append("\n");
|
||||
report.append("Account: ").append(acct.name).append("\n" +
|
||||
" Address book sync. interval: ").append(syncStatus(settings, ContactsContract.AUTHORITY)).append("\n" +
|
||||
" Calendar sync. interval: ").append(syncStatus(settings, CalendarContract.AUTHORITY)).append("\n" +
|
||||
" OpenTasks sync. interval: ").append(syncStatus(settings, "org.dmfs.tasks")).append("\n" +
|
||||
" Preemptive auth: ").append(settings.preemptiveAuth()).append("\n" +
|
||||
" WiFi only: ").append(settings.getSyncWifiOnly());
|
||||
if (settings.getSyncWifiOnlySSID() != null)
|
||||
report.append(", SSID: ").append(settings.getSyncWifiOnlySSID());
|
||||
report.append("\n [CardDAV] Contact group method: ").append(settings.getGroupMethod())
|
||||
.append("\n RFC 6868 encoding: ").append(settings.getVCardRFC6868())
|
||||
.append("\n [CalDAV] Time range (past days): ").append(settings.getTimeRangePastDays())
|
||||
.append("\n Manage calendar colors: ").append(settings.getManageCalendarColors())
|
||||
.append("\n");
|
||||
} catch(InvalidAccountException e) {
|
||||
report.append(acct).append(" is invalid (unsupported settings version) or does not exist\n");
|
||||
}
|
||||
@@ -236,7 +238,7 @@ public class DebugInfoActivity extends AppCompatActivity implements LoaderManage
|
||||
"Android version: ").append(Build.VERSION.RELEASE).append(" (").append(Build.DISPLAY).append(")\n" +
|
||||
"Device: ").append(WordUtils.capitalize(Build.MANUFACTURER)).append(" ").append(Build.MODEL).append(" (").append(Build.DEVICE).append(")\n\n"
|
||||
);
|
||||
} catch (Exception ex) {
|
||||
} catch(Exception ex) {
|
||||
App.log.log(Level.SEVERE, "Couldn't get system details", ex);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ public class ExceptionInfoFragment extends DialogFragment {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Intent intent = new Intent(getContext(), DebugInfoActivity.class);
|
||||
intent.putExtra(DebugInfoActivity.KEY_EXCEPTION, exception);
|
||||
intent.putExtra(DebugInfoActivity.KEY_THROWABLE, exception);
|
||||
if (account != null)
|
||||
intent.putExtra(DebugInfoActivity.KEY_ACCOUNT, account);
|
||||
startActivity(intent);
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.ui;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.View;
|
||||
|
||||
import at.bitfire.davdroid.Constants;
|
||||
import at.bitfire.davdroid.R;
|
||||
import at.bitfire.davdroid.resource.LocalTaskList;
|
||||
|
||||
public class PermissionsActivity extends AppCompatActivity {
|
||||
|
||||
public static final String
|
||||
PERMISSION_READ_TASKS = "org.dmfs.permission.READ_TASKS",
|
||||
PERMISSION_WRITE_TASKS = "org.dmfs.permission.WRITE_TASKS";
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_permissions);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
refresh();
|
||||
}
|
||||
|
||||
protected void refresh() {
|
||||
boolean noCalendarPermissions =
|
||||
ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED ||
|
||||
ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_CALENDAR) != PackageManager.PERMISSION_GRANTED;
|
||||
findViewById(R.id.calendar_permissions).setVisibility(noCalendarPermissions ? View.VISIBLE : View.GONE);
|
||||
|
||||
boolean noContactsPermissions =
|
||||
ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED ||
|
||||
ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED;
|
||||
findViewById(R.id.contacts_permissions).setVisibility(noContactsPermissions ? View.VISIBLE : View.GONE);
|
||||
|
||||
boolean noTaskPermissions;
|
||||
if (LocalTaskList.tasksProviderAvailable(this)) {
|
||||
noTaskPermissions =
|
||||
ActivityCompat.checkSelfPermission(this, PERMISSION_READ_TASKS) != PackageManager.PERMISSION_GRANTED ||
|
||||
ActivityCompat.checkSelfPermission(this, PERMISSION_WRITE_TASKS) != PackageManager.PERMISSION_GRANTED;
|
||||
findViewById(R.id.opentasks_permissions).setVisibility(noTaskPermissions ? View.VISIBLE : View.GONE);
|
||||
} else {
|
||||
findViewById(R.id.opentasks_permissions).setVisibility(View.GONE);
|
||||
noTaskPermissions = false;
|
||||
}
|
||||
|
||||
if (!noCalendarPermissions && !noContactsPermissions && !noTaskPermissions) {
|
||||
NotificationManager nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
|
||||
nm.cancel(Constants.NOTIFICATION_PERMISSIONS);
|
||||
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
public void requestCalendarPermissions(View v) {
|
||||
ActivityCompat.requestPermissions(this, new String[] {
|
||||
Manifest.permission.READ_CALENDAR,
|
||||
Manifest.permission.WRITE_CALENDAR
|
||||
}, 0);
|
||||
}
|
||||
|
||||
public void requestContactsPermissions(View v) {
|
||||
ActivityCompat.requestPermissions(this, new String[] {
|
||||
Manifest.permission.READ_CONTACTS,
|
||||
Manifest.permission.WRITE_CONTACTS
|
||||
}, 0);
|
||||
}
|
||||
|
||||
public void requestOpenTasksPermissions(View v) {
|
||||
ActivityCompat.requestPermissions(this, new String[] {
|
||||
PERMISSION_READ_TASKS,
|
||||
PERMISSION_WRITE_TASKS
|
||||
}, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
@@ -59,18 +59,21 @@ public class StartupDialogFragment extends DialogFragment {
|
||||
dialogs.add(StartupDialogFragment.instantiate(Mode.DEVELOPMENT_VERSION));
|
||||
else {
|
||||
// store-specific information
|
||||
final String installedFrom = installedFrom(context);
|
||||
if (installedFrom == null || installedFrom.startsWith("org.fdroid"))
|
||||
dialogs.add(StartupDialogFragment.instantiate(Mode.FDROID_DONATE));
|
||||
|
||||
else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && // only on Android <5
|
||||
"com.android.vending".equals(installedFrom) && // only when installed from Play Store
|
||||
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.add(StartupDialogFragment.instantiate(Mode.GOOGLE_PLAY_ACCOUNTS_REMOVED));
|
||||
dialogs.add(StartupDialogFragment.instantiate(Mode.GOOGLE_PLAY_ACCOUNTS_REMOVED));
|
||||
} else {
|
||||
// other stores
|
||||
final String installedFrom = installedFrom(context);
|
||||
if (installedFrom == null || installedFrom.startsWith("org.fdroid"))
|
||||
dialogs.add(StartupDialogFragment.instantiate(Mode.FDROID_DONATE));
|
||||
}
|
||||
}
|
||||
|
||||
// OpenTasks information
|
||||
if (!LocalTaskList.tasksProviderAvailable(context.getContentResolver()) &&
|
||||
if (!LocalTaskList.tasksProviderAvailable(context) &&
|
||||
settings.getBoolean(HINT_OPENTASKS_NOT_INSTALLED, true))
|
||||
dialogs.add(StartupDialogFragment.instantiate(Mode.OPENTASKS_NOT_INSTALLED));
|
||||
|
||||
@@ -108,28 +111,31 @@ public class StartupDialogFragment extends DialogFragment {
|
||||
.setNeutralButton(R.string.startup_development_version_give_feedback, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Constants.webUri.buildUpon().appendEncodedPath("forums/beta/").build()));
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Constants.webUri.buildUpon().appendEncodedPath("forums/").build()));
|
||||
}
|
||||
})
|
||||
.create();
|
||||
|
||||
case FDROID_DONATE:
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setIcon(R.drawable.ic_launcher)
|
||||
.setTitle(R.string.startup_donate)
|
||||
.setMessage(R.string.startup_donate_message)
|
||||
.setPositiveButton(R.string.startup_donate_now, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Constants.webUri.buildUpon().appendEncodedPath("donate/").build()));
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.startup_donate_later, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
}
|
||||
})
|
||||
.create();
|
||||
if (BuildConfig.FLAVOR != App.FLAVOR_GOOGLE_PLAY)
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setIcon(R.drawable.ic_launcher)
|
||||
.setTitle(R.string.startup_donate)
|
||||
.setMessage(R.string.startup_donate_message)
|
||||
.setPositiveButton(R.string.startup_donate_now, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Constants.webUri.buildUpon().appendEncodedPath("donate/").build()));
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.startup_donate_later, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
}
|
||||
})
|
||||
.create();
|
||||
else
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
case GOOGLE_PLAY_ACCOUNTS_REMOVED:
|
||||
Drawable icon = null;
|
||||
@@ -164,10 +170,13 @@ public class StartupDialogFragment extends DialogFragment {
|
||||
.create();
|
||||
|
||||
case OPENTASKS_NOT_INSTALLED:
|
||||
StringBuilder builder = new StringBuilder(getString(R.string.startup_opentasks_not_installed_message));
|
||||
if (Build.VERSION.SDK_INT < 23)
|
||||
builder.append("\n\n").append(getString(R.string.startup_opentasks_reinstall_davdroid));
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setIcon(R.drawable.ic_alarm_on_dark)
|
||||
.setTitle(R.string.startup_opentasks_not_installed)
|
||||
.setMessage(R.string.startup_opentasks_not_installed_message)
|
||||
.setMessage(builder.toString())
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
@@ -177,7 +186,10 @@ public class StartupDialogFragment extends DialogFragment {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=org.dmfs.tasks"));
|
||||
getContext().startActivity(intent);
|
||||
if (intent.resolveActivity(getContext().getPackageManager()) != null)
|
||||
getContext().startActivity(intent);
|
||||
else
|
||||
App.log.warning("No market app available, can't install OpenTasks");
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.startup_dont_show_again, new DialogInterface.OnClickListener() {
|
||||
|
||||
@@ -14,6 +14,7 @@ import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.CalendarContract;
|
||||
import android.provider.ContactsContract;
|
||||
@@ -24,6 +25,7 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Spinner;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.logging.Level;
|
||||
@@ -32,6 +34,7 @@ import at.bitfire.davdroid.AccountSettings;
|
||||
import at.bitfire.davdroid.App;
|
||||
import at.bitfire.davdroid.Constants;
|
||||
import at.bitfire.davdroid.DavService;
|
||||
import at.bitfire.davdroid.InvalidAccountException;
|
||||
import at.bitfire.davdroid.R;
|
||||
import at.bitfire.davdroid.model.CollectionInfo;
|
||||
import at.bitfire.davdroid.model.ServiceDB.Collections;
|
||||
@@ -40,11 +43,16 @@ import at.bitfire.davdroid.model.ServiceDB.OpenHelper;
|
||||
import at.bitfire.davdroid.model.ServiceDB.Services;
|
||||
import at.bitfire.davdroid.resource.LocalTaskList;
|
||||
import at.bitfire.ical4android.TaskProvider;
|
||||
import at.bitfire.vcard4android.GroupMethod;
|
||||
import lombok.Cleanup;
|
||||
|
||||
public class AccountDetailsFragment extends Fragment {
|
||||
|
||||
private static final String KEY_CONFIG = "config";
|
||||
private static final int DEFAULT_SYNC_INTERVAL = 4 * 3600; // 4 hours
|
||||
|
||||
Spinner spnrGroupMethod;
|
||||
|
||||
|
||||
public static AccountDetailsFragment newInstance(DavResourceFinder.Configuration config) {
|
||||
AccountDetailsFragment frag = new AccountDetailsFragment();
|
||||
@@ -54,6 +62,7 @@ public class AccountDetailsFragment extends Fragment {
|
||||
return frag;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
final View v = inflater.inflate(R.layout.login_account_details, container, false);
|
||||
@@ -71,6 +80,10 @@ public class AccountDetailsFragment extends Fragment {
|
||||
final EditText editName = (EditText)v.findViewById(R.id.account_name);
|
||||
editName.setText(config.userName);
|
||||
|
||||
// CardDAV-specific
|
||||
v.findViewById(R.id.carddav).setVisibility(config.cardDAV != null ? View.VISIBLE : View.GONE);
|
||||
spnrGroupMethod = (Spinner)v.findViewById(R.id.contact_group_method);
|
||||
|
||||
Button btnCreate = (Button)v.findViewById(R.id.create_account);
|
||||
btnCreate.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
@@ -105,45 +118,60 @@ public class AccountDetailsFragment extends Fragment {
|
||||
App.log.log(Level.INFO, "Writing account configuration to database", config);
|
||||
@Cleanup OpenHelper dbHelper = new OpenHelper(getContext());
|
||||
SQLiteDatabase db = dbHelper.getWritableDatabase();
|
||||
db.beginTransactionNonExclusive();
|
||||
try {
|
||||
AccountSettings settings = new AccountSettings(getContext(), account);
|
||||
|
||||
Intent refreshIntent = new Intent(getActivity(), DavService.class);
|
||||
refreshIntent.setAction(DavService.ACTION_REFRESH_COLLECTIONS);
|
||||
|
||||
if (config.cardDAV != null) {
|
||||
// insert CardDAV service
|
||||
long id = insertService(db, accountName, Services.SERVICE_CARDDAV, config.cardDAV);
|
||||
|
||||
// start CardDAV service detection (refresh collections)
|
||||
refreshIntent.putExtra(DavService.EXTRA_DAV_SERVICE_ID, id);
|
||||
getActivity().startService(refreshIntent);
|
||||
|
||||
// initial CardDAV account settings
|
||||
int idx = spnrGroupMethod.getSelectedItemPosition();
|
||||
String groupMethodName = getResources().getStringArray(R.array.settings_contact_group_method_values)[idx];
|
||||
settings.setGroupMethod(GroupMethod.valueOf(groupMethodName));
|
||||
|
||||
// enable contact sync
|
||||
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
|
||||
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
|
||||
settings.setSyncInterval(ContactsContract.AUTHORITY, DEFAULT_SYNC_INTERVAL);
|
||||
} else
|
||||
// disable contact sync when CardDAV is not available
|
||||
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 0);
|
||||
|
||||
if (config.calDAV != null) {
|
||||
// insert CalDAV service
|
||||
long id = insertService(db, accountName, Services.SERVICE_CALDAV, config.calDAV);
|
||||
|
||||
// start CalDAV service detection (refresh collections)
|
||||
refreshIntent.putExtra(DavService.EXTRA_DAV_SERVICE_ID, id);
|
||||
getActivity().startService(refreshIntent);
|
||||
|
||||
// enable calendar sync
|
||||
ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 1);
|
||||
ContentResolver.setSyncAutomatically(account, CalendarContract.AUTHORITY, true);
|
||||
settings.setSyncInterval(CalendarContract.AUTHORITY, DEFAULT_SYNC_INTERVAL);
|
||||
|
||||
if (LocalTaskList.tasksProviderAvailable(getContext().getContentResolver())) {
|
||||
// will only do something if OpenTasks is installed and accessible
|
||||
// enable task sync, if possible
|
||||
if (Build.VERSION.SDK_INT >= 23 || LocalTaskList.tasksProviderAvailable(getContext())) {
|
||||
ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 1);
|
||||
ContentResolver.setSyncAutomatically(account, TaskProvider.ProviderName.OpenTasks.authority, true);
|
||||
settings.setSyncInterval(TaskProvider.ProviderName.OpenTasks.authority, DEFAULT_SYNC_INTERVAL);
|
||||
} else
|
||||
// If OpenTasks is installed after DAVdroid, DAVdroid won't get task permissions and crash at every task sync
|
||||
// unless we disable task sync here (before OpenTasks is available).
|
||||
// Android <6 only: disable OpenTasks sync forever when OpenTasks is not installed
|
||||
// because otherwise, there will be a non-catchable SecurityException as soon as OpenTasks is installed
|
||||
ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 0);
|
||||
} else {
|
||||
// disable calendar and task sync when CalDAV is not available
|
||||
ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 0);
|
||||
ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 0);
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
} catch(InvalidAccountException e) {
|
||||
App.log.log(Level.SEVERE, "Couldn't access account settings", e);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -157,21 +185,21 @@ public class AccountDetailsFragment extends Fragment {
|
||||
values.put(Services.SERVICE, service);
|
||||
if (info.principal != null)
|
||||
values.put(Services.PRINCIPAL, info.principal.toString());
|
||||
long serviceID = db.insertOrThrow(Services._TABLE, null, values);
|
||||
long serviceID = db.insertWithOnConflict(Services._TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
|
||||
// insert home sets
|
||||
for (URI homeSet : info.homeSets) {
|
||||
values.clear();
|
||||
values.put(HomeSets.SERVICE_ID, serviceID);
|
||||
values.put(HomeSets.URL, homeSet.toString());
|
||||
db.insertOrThrow(HomeSets._TABLE, null, values);
|
||||
db.insertWithOnConflict(HomeSets._TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
}
|
||||
|
||||
// insert collections
|
||||
for (CollectionInfo collection : info.collections.values()) {
|
||||
values = collection.toDB();
|
||||
values.put(Collections.SERVICE_ID, serviceID);
|
||||
db.insertOrThrow(Collections._TABLE, null, values);
|
||||
db.insertWithOnConflict(Collections._TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
}
|
||||
|
||||
return serviceID;
|
||||
|
||||
@@ -190,6 +190,13 @@ public class DavResourceFinder {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If #dav is an address book or an address book home set, it will added to
|
||||
* config.collections or config.homesets. Only evaluates already known properties,
|
||||
* does not call dav.propfind()! URLs will be stored with trailing "/".
|
||||
* @param dav resource whose properties are evaluated
|
||||
* @param config structure where the address book (collection) and/or home set is stored into (if found)
|
||||
*/
|
||||
protected void rememberIfAddressBookOrHomeset(@NonNull DavResource dav, @NonNull Configuration.ServiceInfo config) {
|
||||
// Is the collection an address book?
|
||||
ResourceType resourceType = (ResourceType)dav.properties.get(ResourceType.NAME);
|
||||
@@ -202,8 +209,11 @@ public class DavResourceFinder {
|
||||
// Does the collection refer to address book homesets?
|
||||
AddressbookHomeSet homeSets = (AddressbookHomeSet)dav.properties.get(AddressbookHomeSet.NAME);
|
||||
if (homeSets != null)
|
||||
for (String href : homeSets.hrefs)
|
||||
config.homeSets.add(UrlUtils.withTrailingSlash(dav.location.resolve(href)).uri());
|
||||
for (String href : homeSets.hrefs) {
|
||||
HttpUrl location = UrlUtils.withTrailingSlash(dav.location.resolve(href));
|
||||
log.info("Found addressbook home-set at " + location);
|
||||
config.homeSets.add(location.uri());
|
||||
}
|
||||
}
|
||||
|
||||
protected void rememberIfCalendarOrHomeset(@NonNull DavResource dav, @NonNull Configuration.ServiceInfo config) {
|
||||
@@ -223,7 +233,7 @@ public class DavResourceFinder {
|
||||
}
|
||||
|
||||
|
||||
boolean providesService(HttpUrl url, Service service) throws IOException {
|
||||
protected boolean providesService(HttpUrl url, Service service) throws IOException {
|
||||
DavResource davPrincipal = new DavResource(httpClient, url, log);
|
||||
try {
|
||||
davPrincipal.options();
|
||||
@@ -302,7 +312,7 @@ public class DavResourceFinder {
|
||||
|
||||
if (principal != null)
|
||||
return principal;
|
||||
} catch(NotFoundException e) {
|
||||
} catch(NotFoundException|IllegalArgumentException e) {
|
||||
log.log(Level.WARNING, "No resource found", e);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -22,7 +22,6 @@ import android.support.v4.content.Loader;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
||||
import at.bitfire.davdroid.App;
|
||||
import at.bitfire.davdroid.InvalidAccountException;
|
||||
import at.bitfire.davdroid.R;
|
||||
import at.bitfire.davdroid.ui.DebugInfoActivity;
|
||||
import at.bitfire.davdroid.ui.setup.DavResourceFinder.Configuration;
|
||||
|
||||
@@ -21,10 +21,14 @@ import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RadioButton;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.net.IDN;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import at.bitfire.dav4android.Constants;
|
||||
import at.bitfire.davdroid.R;
|
||||
import at.bitfire.davdroid.ui.widget.EditPassword;
|
||||
|
||||
@@ -83,6 +87,7 @@ public class LoginCredentialsFragment extends Fragment implements CompoundButton
|
||||
boolean loginByEmail = buttonView == radioUseEmail;
|
||||
emailDetails.setVisibility(loginByEmail ? View.VISIBLE : View.GONE);
|
||||
urlDetails.setVisibility(loginByEmail ? View.GONE : View.VISIBLE);
|
||||
(loginByEmail ? editEmailAddress : editBaseURL).requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,11 +123,16 @@ public class LoginCredentialsFragment extends Fragment implements CompoundButton
|
||||
Uri baseUrl = Uri.parse(editBaseURL.getText().toString());
|
||||
String scheme = baseUrl.getScheme();
|
||||
if ("https".equalsIgnoreCase(scheme) || "http".equalsIgnoreCase(scheme)) {
|
||||
String host = IDN.toASCII(baseUrl.getHost());
|
||||
if (host.isEmpty()) {
|
||||
String host = baseUrl.getHost();
|
||||
if (StringUtils.isEmpty(host)) {
|
||||
editBaseURL.setError(getString(R.string.login_url_host_name_required));
|
||||
valid = false;
|
||||
}
|
||||
} else
|
||||
try {
|
||||
host = IDN.toASCII(host);
|
||||
} catch(IllegalArgumentException e) {
|
||||
Constants.log.log(Level.WARNING, "Host name not conforming to RFC 3490", e);
|
||||
}
|
||||
|
||||
String path = baseUrl.getEncodedPath();
|
||||
int port = baseUrl.getPort();
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
||||
~ All rights reserved. This program and the accompanying materials
|
||||
~ are made available under the terms of the GNU Public License v3.0
|
||||
~ which accompanies this distribution, and is available at
|
||||
~ http://www.gnu.org/licenses/gpl.html
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#00ffffff"/>
|
||||
|
||||
<stroke android:width="1dp"
|
||||
android:color="#aa888888"
|
||||
/>
|
||||
|
||||
<padding android:left="1dp"
|
||||
android:top="1dp"
|
||||
android:right="1dp"
|
||||
android:bottom="1dp"
|
||||
/>
|
||||
|
||||
<corners android:bottomRightRadius="8dp" android:bottomLeftRadius="8dp"
|
||||
android:topLeftRadius="8dp" android:topRightRadius="8dp"/>
|
||||
</shape>
|
||||
@@ -46,10 +46,9 @@
|
||||
<TextView
|
||||
android:id="@+id/license_info"
|
||||
tools:text="This program comes with ABSOLUTELY NO WARRANTY. It is free software, and you are welcome to redistribute it under certain conditions."
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:layout_gravity="center_horizontal"/>
|
||||
android:layout_marginBottom="32dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
@@ -68,4 +67,4 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
</ScrollView>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context="at.bitfire.davdroid.ui.AccountsActivity">
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
|
||||
@@ -27,8 +27,7 @@
|
||||
<android.support.design.widget.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||
android:background="@color/light_green700"
|
||||
app:tabIndicatorColor="@color/light_green500"
|
||||
style="@style/tablayout"
|
||||
app:tabMode="scrollable"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
@@ -14,12 +14,10 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:openDrawer="start">
|
||||
|
||||
<include
|
||||
layout="@layout/accounts_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
<include layout="@layout/accounts_content"/>
|
||||
|
||||
<android.support.design.widget.NavigationView
|
||||
android:id="@+id/nav_view"
|
||||
|
||||
103
app/src/main/res/layout/activity_permissions.xml
Normal file
103
app/src/main/res/layout/activity_permissions.xml
Normal file
@@ -0,0 +1,103 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
||||
~ All rights reserved. This program and the accompanying materials
|
||||
~ are made available under the terms of the GNU Public License v3.0
|
||||
~ which accompanies this distribution, and is available at
|
||||
~ http://www.gnu.org/licenses/gpl.html
|
||||
-->
|
||||
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="@dimen/activity_margin">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/calendar_permissions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="16dp"
|
||||
tools:ignore="UselessParent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextView.Heading"
|
||||
android:text="@string/permissions_calendar"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/permissions_calendar_details"/>
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/permissions_calendar_request"
|
||||
android:onClick="requestCalendarPermissions"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/contacts_permissions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="16dp"
|
||||
tools:ignore="UselessParent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextView.Heading"
|
||||
android:text="@string/permissions_contacts"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/permissions_contacts_details"/>
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/permissions_contacts_request"
|
||||
android:onClick="requestContactsPermissions"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/opentasks_permissions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
tools:ignore="UselessParent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextView.Heading"
|
||||
android:text="@string/permissions_opentasks"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/permissions_opentasks_details"/>
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/permissions_opentasks_request"
|
||||
android:onClick="requestOpenTasksPermissions"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
@@ -38,11 +38,32 @@
|
||||
android:inputType="textEmailAddress"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/account_email_hint"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/login_account_name_info"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/carddav"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/login_account_contact_group_method"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/contact_group_method"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:entries="@array/settings_contact_group_method_entries"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<!--DavService-->
|
||||
<!--AppSettingsActivity-->
|
||||
<!--AccountActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_type_email">Entra amb una adreça de correu electrònic</string>
|
||||
<string name="login_type_url">Entra amb una URL i un nom d\'usuari</string>
|
||||
|
||||
@@ -3,19 +3,120 @@
|
||||
<!--common strings-->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="help">Pomoc</string>
|
||||
<string name="manage_accounts">Spravovat účty</string>
|
||||
<string name="please_wait">Chvíli strpení ...</string>
|
||||
<string name="send">Odeslat</string>
|
||||
<!--startup dialogs-->
|
||||
<string name="startup_dont_show_again">Již nezobrazovat</string>
|
||||
<string name="startup_development_version">DAVdroid preview vydání</string>
|
||||
<string name="startup_development_version_message">Toto je vývojová verze aplikace DAVdroid. Mějte na paměti, že vše nemusí správně fungovat. Budeme rádi za konstruktivní zpětnou vazbu, která pomůže vylepšit DAVdroid.</string>
|
||||
<string name="startup_development_version_give_feedback">Dát zpětnou vazbu</string>
|
||||
<string name="startup_donate">Open Source informace</string>
|
||||
<string name="startup_donate_message">Jsme velice rádi že používáte DAVdroid, software s otevřeným zdrojovým kódem (GPLv3). Vývoj této aplikace je náročný a trval již několik tisíc hodin, velice nás potěší přispějete-li na jeho vývoj.</string>
|
||||
<string name="startup_donate_now">Zobrazit stránku pro obdarování</string>
|
||||
<string name="startup_donate_later">Možná později</string>
|
||||
<string name="startup_google_play_accounts_removed">Informace o chybě DRM Obchodu Play</string>
|
||||
<string name="startup_google_play_accounts_removed_message">Za určitých podmínek může dojít po restartu nebo aktualizaci aplikace DAVdroid k vymazání účtů kvůli chybě DRM Obchodu Play. Pokud jste postiženi touto chybou (ale pouze v tomto případě), nainstalujte prosím z Obchodu Play aplikaci \"DAVdroid JB Workaround\".</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Více informací</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks není nainstalován</string>
|
||||
<string name="startup_opentasks_not_installed_message">Aplikace OpenTasks není dostupná, proto nebude DAVdroid moci synchronizovat seznam úkolů.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Po instalaci OpenTasks musíte PŘEINSTALOVAT DAVdroid a přidat znovu své účty (Android chyba).</string>
|
||||
<string name="startup_opentasks_not_installed_install">Nainstalovat OpenTasks</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Licenční podmínky</string>
|
||||
<string name="about_license_info_no_warranty">Tento program je distribuován BEZ JAKÉKOLIV ZÁRUKY. Je to volně dostupný software a lze jej za určitých podmínek dále distribuovat.</string>
|
||||
<!--global settings-->
|
||||
<string name="logging_davdroid_file_logging">DAVdroid logování do souboru</string>
|
||||
<string name="logging_to_external_storage">Logování do externího úložiště: %s</string>
|
||||
<string name="logging_to_external_storage_warning">Smazat logy jak nejdříve možno!</string>
|
||||
<string name="logging_couldnt_create_file">Nelze vytvořit externí soubor logu: %s</string>
|
||||
<string name="logging_no_external_storage">Externí úložiště nenalezeno</string>
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_open">Otevřít panel navigace</string>
|
||||
<string name="navigation_drawer_close">Zavřít panel navigace</string>
|
||||
<string name="navigation_drawer_subtitle">CalDAV/CardDAV adapter synchronizace</string>
|
||||
<string name="navigation_drawer_about">O aplikaci / Licence</string>
|
||||
<string name="navigation_drawer_settings">Nastavení</string>
|
||||
<string name="navigation_drawer_news_updates">Novinky & aktualizace</string>
|
||||
<string name="navigation_drawer_external_links">Externí odkazy</string>
|
||||
<string name="navigation_drawer_website">Webová stránka</string>
|
||||
<string name="navigation_drawer_faq">FAQ</string>
|
||||
<string name="navigation_drawer_forums">Komunita</string>
|
||||
<string name="navigation_drawer_donate">Obdarovat</string>
|
||||
<string name="account_list_empty">Vítejte v aplikaci DAVdroid!\n\nNyní můžete přidat CalDAV/CardDAV účet.</string>
|
||||
<!--DavService-->
|
||||
<string name="dav_service_refresh_failed">Vyhledání služby selhalo</string>
|
||||
<string name="dav_service_refresh_couldnt_refresh">Nelze obnovit seznam sbírky</string>
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">Nastavení</string>
|
||||
<string name="app_settings_user_interface">Uživatelské prostředí</string>
|
||||
<string name="app_settings_reset_hints">Resetovat nápovědu</string>
|
||||
<string name="app_settings_reset_hints_summary">Znovu povolí vypnuté texty nápovědy</string>
|
||||
<string name="app_settings_reset_hints_success">Budou zobrazovány všechny texty nápovědy</string>
|
||||
<string name="app_settings_security">Zabezpečení</string>
|
||||
<string name="app_settings_reset_trusted_certificates">Resetovat důvěryhodné certifikáty</string>
|
||||
<string name="app_settings_reset_trusted_certificates_summary">Zapomene všechny dříve akceptované certifikáty</string>
|
||||
<plurals name="app_settings_reset_trusted_certificates_success">
|
||||
<item quantity="one">Znedůvěryhodněn jeden certifikát</item>
|
||||
<item quantity="few">Znedůvěryhodněny %d certifikáty</item>
|
||||
<item quantity="other">Znedůvěryhodněno %d certifikátů</item>
|
||||
</plurals>
|
||||
<string name="app_settings_debug">Ladění</string>
|
||||
<string name="app_settings_log_to_external_storage">Logovat do externího souboru</string>
|
||||
<string name="app_settings_log_to_external_storage_on">Logování do externího úložiště (pokud dostupné)</string>
|
||||
<string name="app_settings_log_to_external_storage_off">Logování do externího souboru je vypnuto</string>
|
||||
<string name="app_settings_show_debug_info">Zobrazit ladící informace</string>
|
||||
<string name="app_settings_show_debug_info_details">Zobrazit/sdílet software a detaily konfigurace</string>
|
||||
<!--AccountActivity-->
|
||||
<string name="account_synchronize_now">Synchronizovat nyní</string>
|
||||
<string name="account_synchronizing_now">Probíhá synchronizace</string>
|
||||
<string name="account_settings">Nastavení účtu</string>
|
||||
<string name="account_delete">Smazat účet</string>
|
||||
<string name="account_delete_confirmation_title">Opravdu smazat účet?</string>
|
||||
<string name="account_delete_confirmation_text">Všechny místní kopie adresáře, kalendářů a úkolů budou smazány.</string>
|
||||
<string name="account_refresh_address_book_list">Obnovit seznam adresářů</string>
|
||||
<string name="account_create_new_address_book">Vytvořit nový adresář</string>
|
||||
<string name="account_refresh_calendar_list">Obnovit seznam kalendářů</string>
|
||||
<string name="account_create_new_calendar">Vytvořit nový kalendář</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">DAVdroid oprávnění</string>
|
||||
<string name="permissions_calendar">Oprávnění pro kalendáře</string>
|
||||
<string name="permissions_calendar_details">Pro synchronizaci CalDAV událostí s místním kalendářem potřebuje DAVdroid oprávnění přistupovat ke kalendářům.</string>
|
||||
<string name="permissions_calendar_request">Vyžádat oprávnění kalendáře</string>
|
||||
<string name="permissions_contacts">Oprávnění pro kontakty</string>
|
||||
<string name="permissions_contacts_details">Pro synchronizaci CardDAV adresářů s místními kontakty potřebuje DAVdroid oprávnění přistupovat ke kontaktům.</string>
|
||||
<string name="permissions_contacts_request">Vyžádat oprávnění kontaktů</string>
|
||||
<string name="permissions_opentasks">Oprávnění pro OpenTasks</string>
|
||||
<string name="permissions_opentasks_details">Pro synchronizaci CalDAV událostí s místním seznamem úkolů potřebuje DAVdroid oprávnění přistupovat k OpenTasks.</string>
|
||||
<string name="permissions_opentasks_request">Vyžádat oprávnění OpenTasks</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">Přidat účet</string>
|
||||
<string name="login_type_email">Přihlášení s emailovou adresou</string>
|
||||
<string name="login_email_address">Emailová adresa</string>
|
||||
<string name="login_email_address_error">Vyžadován platný email</string>
|
||||
<string name="login_password">Heslo</string>
|
||||
<string name="login_password_required">Vyžadováno heslo</string>
|
||||
<string name="login_type_url">Přihlášení s URL a uživatelským jménem</string>
|
||||
<string name="login_url_must_be_http_or_https">URL musí začínat na http(s)://</string>
|
||||
<string name="login_url_host_name_required">Vyžadováno hostname</string>
|
||||
<string name="login_user_name">Uživatelské jméno</string>
|
||||
<string name="login_user_name_required">Vyžadováno uživatelské jméno</string>
|
||||
<string name="login_base_url">Základní URL</string>
|
||||
<string name="login_auth_preemptive">Preemptivní ověření (doporučeno, ale není kompatibilní s Digest ověřením)</string>
|
||||
<string name="login_login">Login</string>
|
||||
<string name="login_back">Zpět</string>
|
||||
<string name="login_create_account">Vytvořit účet</string>
|
||||
<string name="login_account_name">Jméno účtu</string>
|
||||
<string name="login_account_name_info">Pro jméno účtu použijte svou emailovou adresu, protože Android bude brát jméno účtu jako údaj pro ORGANIZÁTORA vytvořených událostí. Nelze mít dva účty stejného jména.</string>
|
||||
<string name="login_account_contact_group_method">Metoda seskupování kontaktů:</string>
|
||||
<string name="login_account_name_required">Vyžadováno jméno účtu</string>
|
||||
<string name="login_account_not_created">Účet nelze vytvořit</string>
|
||||
<string name="login_configuration_detection">Vyhledání konfigurace</string>
|
||||
<string name="login_querying_server">Chvíli strpení, probíhá dotazování serveru...</string>
|
||||
<string name="login_no_caldav_carddav">Nelze nalézt službu CalDAV nebo CardDAV.</string>
|
||||
<string name="login_view_logs">Prohlížet logy</string>
|
||||
<!--AccountSettingsActivity-->
|
||||
<string name="settings_title">Nastavení: %s</string>
|
||||
<string name="settings_authentication">Ověření</string>
|
||||
<string name="settings_username">Uživatelské jméno</string>
|
||||
<string name="settings_enter_username">Zadat uživatelské jméno</string>
|
||||
@@ -52,10 +153,71 @@
|
||||
<item>Každé 4 hodiny</item>
|
||||
<item>Jednou za den</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">Synchronizovat pouze přes WiFi</string>
|
||||
<string name="settings_sync_wifi_only_on">Synchronizace omezena na WiFi připojení</string>
|
||||
<string name="settings_sync_wifi_only_off">Druh připojení není brán v potaz</string>
|
||||
<string name="settings_sync_wifi_only_ssid">Omezení WiFi SSID</string>
|
||||
<string name="settings_sync_wifi_only_ssid_on">Synchronizace pouze přes %s</string>
|
||||
<string name="settings_sync_wifi_only_ssid_off">Použít všechna WiFi připojení</string>
|
||||
<string name="settings_sync_wifi_only_ssid_message">Zadejte jméno WiFi sítě (SSID) pro omezení synchronizace na tutu síť, nebo ponechte prázdné pro použití všech WiFi připojení.</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">Metoda seskupování kontaktů</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string-array name="settings_contact_group_method_entries">
|
||||
<item>Skupiny jsou oddělené soubory VCard</item>
|
||||
<item>Skupiny jsou kategorie na kontakt</item>
|
||||
</string-array>
|
||||
<string name="settings_rfc6868_for_vcards">Použít RFC6868 pro VCard</string>
|
||||
<string name="settings_rfc6868_for_vcards_on">Uvozovky lze použít v hodnotách parametrů</string>
|
||||
<string name="settings_rfc6868_for_vcards_off">Uvozovky nelze použít v hodnotách parametrů</string>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Časový limit pro staré události</string>
|
||||
<string name="settings_sync_time_range_past_none">Synchronizovat všechny události</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="one">Ignorovat události starší než 1 den</item>
|
||||
<item quantity="few">Ignorovat události starší než %d dny</item>
|
||||
<item quantity="other">Ignorovat události starší než %d dnů</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Události z minulosti starší než vyznačený počet dnů budou ignorovány (lze zadat 0). Ponechte prázdné pro synchronizaci všech událostí.</string>
|
||||
<string name="settings_manage_calendar_colors">Spravovat barvy kalendářů</string>
|
||||
<string name="settings_manage_calendar_colors_on">Barvy kalendářů spravuje DAVdroid</string>
|
||||
<string name="settings_manage_calendar_colors_off">Barvy kalendářů nespravuje DAVdroid</string>
|
||||
<string name="settings_version_update">Aktualizace verze DAVdroid</string>
|
||||
<string name="settings_version_update_settings_updated">Vnitřní nastavení byla aktualizována.</string>
|
||||
<string name="settings_version_update_install_hint">Při problémech odinstalujte a znovu nainstalujte DAVdroid.</string>
|
||||
<!--collection management-->
|
||||
<string name="create_addressbook">Vytvořit adresář</string>
|
||||
<string name="create_addressbook_display_name_hint">Můj adresář</string>
|
||||
<string name="create_calendar">Vytvořit CalDAV sbírku</string>
|
||||
<string name="create_calendar_display_name_hint">Můj kalendář</string>
|
||||
<string name="create_calendar_time_zone">Časová zóna:</string>
|
||||
<string name="create_calendar_type">Typ sbírky:</string>
|
||||
<string name="create_calendar_type_only_events">Kalendář (pouze události)</string>
|
||||
<string name="create_calendar_type_only_tasks">Seznam úkolů (pouze úkoly)</string>
|
||||
<string name="create_calendar_type_events_and_tasks">Kombinovaná (události a úkoly)</string>
|
||||
<string name="create_collection_color">Nastavit barvu sbírky</string>
|
||||
<string name="create_collection_creating">Vytváření sbírky</string>
|
||||
<string name="create_collection_display_name">Zobrazit jméno (nadpis) této sbírky:</string>
|
||||
<string name="create_collection_display_name_required">Nadpis je vyžadován</string>
|
||||
<string name="create_collection_description">Popis (volitelný):</string>
|
||||
<string name="create_collection_home_set">Domácí sbírka:</string>
|
||||
<string name="create_collection_create">Vytvořit</string>
|
||||
<string name="delete_collection">Smazat sbírku</string>
|
||||
<string name="delete_collection_confirm_title">Jste si jisti?</string>
|
||||
<string name="delete_collection_confirm_warning">Tato sbírka (%s) a všechna její data budou odstraněna ze serveru.</string>
|
||||
<string name="delete_collection_deleting_collection">Mazání sbírky</string>
|
||||
<!--ExceptionInfoFragment-->
|
||||
<string name="exception">Došlo k chybě.</string>
|
||||
<string name="exception_httpexception">Došlo k HTTP chybě.</string>
|
||||
<string name="exception_ioexception">Došlo k I/O chybě.</string>
|
||||
<string name="exception_show_details">Zobrazit detaily</string>
|
||||
<!--sync errors and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Ladící informace</string>
|
||||
<string name="sync_error_permissions">DAVdroid oprávnění</string>
|
||||
<string name="sync_error_permissions_text">Vyžadována dodatečná oprávnění</string>
|
||||
<string name="sync_error_calendar">Synchronizace kalendáře selhala (%s)</string>
|
||||
<string name="sync_error_contacts">Synchronizace adresáře selhala (%s)</string>
|
||||
<string name="sync_error_tasks">Synchronizace úkolu selhala (%s)</string>
|
||||
@@ -65,7 +227,7 @@
|
||||
<string-array name="sync_error_phases">
|
||||
<item>příprava synchronizace</item>
|
||||
<item>dotazování možností</item>
|
||||
<item>zpracovávání místních smazaných záznamů</item>
|
||||
<item>zpracovávání místně smazaných záznamů</item>
|
||||
<item>příprava vytvořených/upravených záznamů</item>
|
||||
<item>nahrávání vytvořených/upravených záznamů</item>
|
||||
<item>kontrola stavu synchronizace</item>
|
||||
@@ -73,6 +235,7 @@
|
||||
<item>výpis vzdálených záznamů</item>
|
||||
<item>porovnání místních/vzdálených záznamů</item>
|
||||
<item>stahování vzdálených záznamů</item>
|
||||
<item>uzavírání procesu</item>
|
||||
<item>ukládání stavu synchronizace</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">Chybné uživatelské jméno/heslo</string>
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
<string name="startup_google_play_accounts_removed_message">Under visse tekniske omstændigheder kan DRM fra Play Store bevirke, at alle DAVdroid-konti er væk efter en genstart eller opgradering af DAVdroid. Hvis du er udsat for dette problem (og ellers ikke), opfordres du til at installere DAVDroid JB Workaround\" fra Play Store.</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Yderligere oplysninger</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks ikke installeret</string>
|
||||
<string name="startup_opentasks_not_installed_message">OpenTasks-app\'en er ikke installeret eller kan ikke tilgås, så DAVdroid vil ikke være i stand til at opdatere opgavelister. Geninstaller DAVdroid og tilføj dine konti igen efter installation af OpenTasks.</string>
|
||||
<string name="startup_opentasks_not_installed_message">OpenTasks er ikke til rådighedm så DAVdroid vil ikke kunne synkronisere opgavelister.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Efter at have installeret OpenTasks, vil du være nødt til at GENINSTALLERE DAVdroid og dine konti igen (en fejl i Android).</string>
|
||||
<string name="startup_opentasks_not_installed_install">Installer OpenTasks</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Licensforhold</string>
|
||||
@@ -76,6 +77,17 @@
|
||||
<string name="account_create_new_address_book">Opret ny adressebog</string>
|
||||
<string name="account_refresh_calendar_list">Opdater kalenderliste</string>
|
||||
<string name="account_create_new_calendar">Opret ny kalender</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">DAVdroid adgangsrettigheder</string>
|
||||
<string name="permissions_calendar">Kalenderadgange</string>
|
||||
<string name="permissions_calendar_details">For at synkronisere CalDAV-begivenheder med dine lokale kalendere, skal DAVdroid have adgang til dine kalendere.</string>
|
||||
<string name="permissions_calendar_request">Anmod om kalenderadgang</string>
|
||||
<string name="permissions_contacts">Kontakter: Adgangsrettigheder</string>
|
||||
<string name="permissions_contacts_details">DAVdroid er nødt til at have adgang til dine kontakter, hvis CardDAV-adressebøger skal kunne synkronisere med dine kontakter.</string>
|
||||
<string name="permissions_contacts_request">Anmod om adgang til kontakter</string>
|
||||
<string name="permissions_opentasks">OpenTasks: Adgangsrettigheder</string>
|
||||
<string name="permissions_opentasks_details">DAVdroid er nødt til at have adgang til OpenTasks, hvis CalDAV-opgaver skal kunne synkronisere med dine lokale opgavelister.</string>
|
||||
<string name="permissions_opentasks_request">Anmod om adgang til OpenTasks</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">Tilføj konto</string>
|
||||
<string name="login_type_email">Log ind med emailadresse</string>
|
||||
@@ -95,6 +107,7 @@
|
||||
<string name="login_create_account">Opret konto</string>
|
||||
<string name="login_account_name">Kontonavn</string>
|
||||
<string name="login_account_name_info">Brug emailadressen som kontonavn, for Android vil bruge kontonavnet til ORGANIZER-feltet for aktiviteter, som du opretter. Du kan ikke have to konti med samme navn.</string>
|
||||
<string name="login_account_contact_group_method">Gruppering af kontakter:</string>
|
||||
<string name="login_account_name_required">Kontonavn påkrævet</string>
|
||||
<string name="login_account_not_created">Konto kunne ikke oprettes</string>
|
||||
<string name="login_configuration_detection">Check konfiguration</string>
|
||||
@@ -139,6 +152,27 @@
|
||||
<item>En gang hver 4. time</item>
|
||||
<item>En gang om dagen</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">Synkroniser kun over WiFi</string>
|
||||
<string name="settings_sync_wifi_only_on">Synkronisering er begrænset til WiFi-forbindelser</string>
|
||||
<string name="settings_sync_wifi_only_off">Forbindelsestypen har ingen betydning</string>
|
||||
<string name="settings_sync_wifi_only_ssid">Begræsning til WiFi-SSID</string>
|
||||
<string name="settings_sync_wifi_only_ssid_on">Vil kun blive synkroniseret over %s</string>
|
||||
<string name="settings_sync_wifi_only_ssid_off">Alle WiFi-forbindelse vil kunne bruges</string>
|
||||
<string name="settings_sync_wifi_only_ssid_message">Indtast navnet på et WiFi-netværk (SSID) for at begrænse synkronisering til dette netværk, eller efterlad feltet blank for at acceptere alle WiFi-forbindelser.</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">Gruppering af kontakter</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string-array name="settings_contact_group_method_entries">
|
||||
<item>Grupper er særskilte VCards</item>
|
||||
<item>Grupper er kategorier pr. kontakt</item>
|
||||
</string-array>
|
||||
<string name="settings_rfc6868_for_vcards">Anvend RFC6868 for VCards</string>
|
||||
<string name="settings_rfc6868_for_vcards_on">Dobbelte citationstegn kan bruges til at sætte parametre</string>
|
||||
<string name="settings_rfc6868_for_vcards_off">Dobbelte citationstegn kan ikke bruges til at sætte parametre</string>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Tidsafgrænsning for tidligere begivenheder</string>
|
||||
<string name="settings_sync_time_range_past_none">Alle begivenheder vil blive synkroniseret</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
@@ -146,6 +180,9 @@
|
||||
<item quantity="other">Begivenheder, der er mere end %d dage gamle, vil blive ignoreret</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Begivenheder, som er mere end dette antal dage gamle vil blive ignoreret (kan også være 0). Hvis feltet ikke er udfyldt, vil alle begivenheder blive synkroniseret.</string>
|
||||
<string name="settings_manage_calendar_colors">Administrer farver for kalender</string>
|
||||
<string name="settings_manage_calendar_colors_on">Kalenderfarver administreres af DAVdroid</string>
|
||||
<string name="settings_manage_calendar_colors_off">Kalenderfarver sættes ikke fra DAVdroid</string>
|
||||
<string name="settings_version_update">Versionsopdatering af DAVdroid</string>
|
||||
<string name="settings_version_update_settings_updated">Interne indstillinger er blevet opdateret.</string>
|
||||
<string name="settings_version_update_install_hint">Problemer? Afinstaller DAVdroid og geninstaller.</string>
|
||||
@@ -177,6 +214,8 @@
|
||||
<string name="exception_show_details">Vis detaljer</string>
|
||||
<!--sync errors and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Debug-info</string>
|
||||
<string name="sync_error_permissions">DAVdroid-rettigheder</string>
|
||||
<string name="sync_error_permissions_text">Yderligere adgang påkrævet</string>
|
||||
<string name="sync_error_calendar">Synkronisering af kalenderen lykkedes ikke (%s)</string>
|
||||
<string name="sync_error_contacts">Synkronisering af adressebogen lykkedes ikke (%s)</string>
|
||||
<string name="sync_error_tasks">Synkronisering af opgaver lykkedes ikke (%s)</string>
|
||||
@@ -185,15 +224,16 @@
|
||||
<string name="sync_error_local_storage">Databasefejl under %s</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>forbereder synkronisering</item>
|
||||
<item>checker funktioner</item>
|
||||
<item>checker understøttelse</item>
|
||||
<item>behandler poster, der er slettet lokalt</item>
|
||||
<item>behandler poster, der er oprettet/ændret lokalt</item>
|
||||
<item>uploader poster, der er oprettet/ændret lokalt</item>
|
||||
<item>behandler poster, der er blevet oprettet/redigeret</item>
|
||||
<item>uploader poster, der er blevet oprettet/redigeret</item>
|
||||
<item>checker synkroniseringsstatus</item>
|
||||
<item>oplister lokale poster</item>
|
||||
<item>oplister poster på server</item>
|
||||
<item>laver liste af lokale poster</item>
|
||||
<item>laver lister af poster på server</item>
|
||||
<item>sammenligner poster lokalt og på server</item>
|
||||
<item>downloader poster på server</item>
|
||||
<item>efterbehandler</item>
|
||||
<item>gemmer synkroniseringsstatus</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">Fejl i brugernavn/adgangskode</string>
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
<string name="startup_google_play_accounts_removed_message">Unter bestimmten Umständen verursacht die zwangsweise Play Store-Verschlüsselung, dass Accounts nach einem Neustart oder einem DAVdroid-Update verschwinden. Installieren Sie \"DAVdroid JB Workaround\", falls Sie von dem Problem betroffen sind.</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Weitere Infos</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks nicht installiert</string>
|
||||
<string name="startup_opentasks_not_installed_message">Die OpenTasks-App ist nicht installiert oder DAVdroid hat keine Rechte, um darauf zuzugreifen. Aufgabenlisten werden also nicht synchronisiert. Falls Sie OpenTasks installieren, müssen Sie DAVdroid DANACH NEU INSTALLIEREN und Ihre Konten neu hinzufügen (Android-Bug).</string>
|
||||
<string name="startup_opentasks_not_installed_message">Die OpenTasks-App ist nicht verfügbar; Aufgabenlisten können also nicht synchronisiert werden.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Nach der Installation von OpenTasks muss DAVdroid NEU INSTALLIERT und die Konten neu hinzugefügt werden (Android-Bug).</string>
|
||||
<string name="startup_opentasks_not_installed_install">OpenTasks installieren</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Lizenzbedingungen</string>
|
||||
@@ -76,6 +77,17 @@
|
||||
<string name="account_create_new_address_book">Neues Adressbuch erstellen</string>
|
||||
<string name="account_refresh_calendar_list">Kalender neu erkennen</string>
|
||||
<string name="account_create_new_calendar">Neuen Kalender erstellen</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">DAVdroid-Berechtigungen</string>
|
||||
<string name="permissions_calendar">Kalender-Zugriff</string>
|
||||
<string name="permissions_calendar_details">Um CalDAV-Termine mit lokalen Kalendern abzugleichen, benötigt DAVdroid Kalender-Zugriffsrechte.</string>
|
||||
<string name="permissions_calendar_request">Kalender-Zugriff anfordern</string>
|
||||
<string name="permissions_contacts">Adressbuch-Zugriff</string>
|
||||
<string name="permissions_contacts_details">Um CardDAV-Kontakte mit dem lokalen Adressbuch abzugleichen, benötigt DAVdroid Adressbuch-Zugriffsrechte.</string>
|
||||
<string name="permissions_contacts_request">Adressbuch-Zugriff anfordern</string>
|
||||
<string name="permissions_opentasks">OpenTasks-Zugriff</string>
|
||||
<string name="permissions_opentasks_details">Um CalDAV-Aufgaben mit lokalen Aufgabenlisten abzugleichen, benötigt DAVdroid Zugriffsrechte für OpenTasks.</string>
|
||||
<string name="permissions_opentasks_request">OpenTasks-Zugriff anfordern</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">Konto hinzufügen</string>
|
||||
<string name="login_type_email">Mit Email-Adresse anmelden</string>
|
||||
@@ -95,6 +107,7 @@
|
||||
<string name="login_create_account">Konto anlegen</string>
|
||||
<string name="login_account_name">Kontoname</string>
|
||||
<string name="login_account_name_info">Verwenden Sie Ihre Email-Adresse als Kontonamen, da Android den Kontonamen als ORGANIZER einsetzt. Es kann allerdings keine zwei Konten mit dem gleichen Namen geben.</string>
|
||||
<string name="login_account_contact_group_method">Kontaktgruppen-Methode:</string>
|
||||
<string name="login_account_name_required">Kontoname wird benötigt</string>
|
||||
<string name="login_account_not_created">Konto konnte nicht angelegt werden</string>
|
||||
<string name="login_configuration_detection">Ressourcen-Erkennung</string>
|
||||
@@ -139,6 +152,27 @@
|
||||
<item>Alle 4 Stunden</item>
|
||||
<item>Täglich</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">Nur über WLAN synchronisieren</string>
|
||||
<string name="settings_sync_wifi_only_on">Synchronisierung nur bei aktiver WLAN-Verbindung</string>
|
||||
<string name="settings_sync_wifi_only_off">Verbindungstyp wird nicht beachtet</string>
|
||||
<string name="settings_sync_wifi_only_ssid">WLAN-SSID-Beschränkung</string>
|
||||
<string name="settings_sync_wifi_only_ssid_on">Synchronisierung nur über %s</string>
|
||||
<string name="settings_sync_wifi_only_ssid_off">Alle WLAN-Verbindungen können verwendet werden</string>
|
||||
<string name="settings_sync_wifi_only_ssid_message">Name eines WLANs (SSID) eingeben, um Synchronisierung auf dieses WLAN zu beschränken. Leer lassen, um alle WLAN-Verbindungen zuzulassen.</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">Kontaktgruppen-Methode</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string-array name="settings_contact_group_method_entries">
|
||||
<item>Gruppen sind eigene VCards</item>
|
||||
<item>Gruppen sind Kategorien der Kontakte</item>
|
||||
</string-array>
|
||||
<string name="settings_rfc6868_for_vcards">RFC6868 für VCards verwenden</string>
|
||||
<string name="settings_rfc6868_for_vcards_on">Anführungszeichen können in Parameter-Werten verwendet werden</string>
|
||||
<string name="settings_rfc6868_for_vcards_off">Anführungszeichen können nicht in Parameter-Werten verwendet werden</string>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Abrufbeschränkung vergangener Termine</string>
|
||||
<string name="settings_sync_time_range_past_none">Alle Termine werden synchronisiert</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
@@ -146,6 +180,9 @@
|
||||
<item quantity="other">Termine, die mehr als %d Tage in der Vergangenheit liegen, werden ignoriert</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Termine, die mehr als diese Anzahl von Tagen in der Vergangenheit liegen, werden ignoriert (kann 0 sein). Feld leer lassen, um alle Termine zu synchronisieren.</string>
|
||||
<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_version_update">Neue DAVdroid-Version</string>
|
||||
<string name="settings_version_update_settings_updated">Interne Einstellungen wurden aktualisiert.</string>
|
||||
<string name="settings_version_update_install_hint">Probleme? DAVdroid löschen und neu installieren.</string>
|
||||
@@ -177,6 +214,8 @@
|
||||
<string name="exception_show_details">Details anzeigen</string>
|
||||
<!--sync errors and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Informationen zur Fehlersuche</string>
|
||||
<string name="sync_error_permissions">DAVdroid-Berechtigungen</string>
|
||||
<string name="sync_error_permissions_text">Zusätzliche Berechtigungen benötigt</string>
|
||||
<string name="sync_error_calendar">Kalender-Synchronisierung fehlgeschlagen (%s)</string>
|
||||
<string name="sync_error_contacts">Adressbuch-Synchronisierung fehlgeschlagen (%s)</string>
|
||||
<string name="sync_error_tasks">Aufgaben-Synchronisierung fehlgeschlagen (%s)</string>
|
||||
@@ -192,9 +231,10 @@
|
||||
<item>Abfragen des Synchronisierungs-Zustands</item>
|
||||
<item>Auflisten lokaler Einträge</item>
|
||||
<item>Auflisten der Server-Einträge</item>
|
||||
<item>Vergleichen tw. Datenbank/Server</item>
|
||||
<item>Vergleich lokaler/Server-Einträge</item>
|
||||
<item>Herunterladen von Server-Einträgen</item>
|
||||
<item>Speichern des Synchronisierungs-Zustands</item>
|
||||
<item>Nachverarbeitung</item>
|
||||
<item>Speichern des Synchronisierungzustands</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">Benutzername/Passwort falsch</string>
|
||||
</resources>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<string name="send">Enviar</string>
|
||||
<!--startup dialogs-->
|
||||
<string name="startup_dont_show_again">No mostrar de nuevo</string>
|
||||
<string name="startup_development_version">Versión candidata final de DAVdroid</string>
|
||||
<string name="startup_development_version_message">Esta es una versión de desarrollo de DAVdroid. Tenga presente que puede que no todo funcione como espera. Por favor, denos una retroalimentación constructiva para mejorar DAVdroid.</string>
|
||||
<string name="startup_development_version_give_feedback">Dar retroalimentación</string>
|
||||
<string name="startup_donate">Información de código abierto</string>
|
||||
@@ -18,9 +19,12 @@
|
||||
<string name="startup_google_play_accounts_removed_message">Bajo ciertas condiciones, el DRM de Play Store puede causar que todas las cuentas de DAVdroid se desconfiguren tras un reinicio o una actualización de DAVdroid. Si esto le afecta (y sólo en ese caso), por favor, instale \"DAVdroid JB Workaround\" desde Play Store.</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Más información</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks no está instalado</string>
|
||||
<string name="startup_opentasks_not_installed_message">La aplicación OpenTasks no está instalada o accesible, por lo que DAVdroid no podrá sincronizar las listas de tareas. Reinstale DAVdroid y añada sus cuentas de nuevo después de instalar OpenTasks.</string>
|
||||
<string name="startup_opentasks_not_installed_message">La aplicación OpenTasks no está disponible. DAVdroid no podrá sincronizar listas de tareas.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Tras instalar OpenTasks, tendrás que re-instalar DAVdroid y añadir tus cuentas de nuevo (por un error de Android).</string>
|
||||
<string name="startup_opentasks_not_installed_install">Instalar OpenTasks</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Términos de la licencia</string>
|
||||
<string name="about_license_info_no_warranty">Este programa viene sin NINGÚN TIPO DE GARANTÍA. Es software libre, y cualquier contribución es bienvenida y redistribuida bajo ciertas condiciones.</string>
|
||||
<!--global settings-->
|
||||
<string name="logging_davdroid_file_logging">Archivo de registro de DAVdroid</string>
|
||||
<string name="logging_to_external_storage">Registrar en almacenamiento externo: %s</string>
|
||||
@@ -28,9 +32,12 @@
|
||||
<string name="logging_couldnt_create_file">No se puede crear el archivo de registro externo: %s</string>
|
||||
<string name="logging_no_external_storage">Almacenamiento externo no encontrado</string>
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_open">Abrir panel de navegación</string>
|
||||
<string name="navigation_drawer_close">Cerrar panel de navegación</string>
|
||||
<string name="navigation_drawer_subtitle">Adaptador de sincronización CalDAV/CardDAV</string>
|
||||
<string name="navigation_drawer_about">Acerca de / Licencia</string>
|
||||
<string name="navigation_drawer_settings">Ajustes</string>
|
||||
<string name="navigation_drawer_news_updates">Noticias y actualizaciones</string>
|
||||
<string name="navigation_drawer_external_links">Enlaces externos</string>
|
||||
<string name="navigation_drawer_website">Sitio web</string>
|
||||
<string name="navigation_drawer_faq">Preguntas frequentes</string>
|
||||
@@ -38,13 +45,27 @@
|
||||
<string name="navigation_drawer_donate">Donar</string>
|
||||
<string name="account_list_empty">Bienvenido a DAVdroid!\n\nAhora puede añadir una cuenta CalDAV/CardDAV.</string>
|
||||
<!--DavService-->
|
||||
<string name="dav_service_refresh_failed">Falló la detección del servicio</string>
|
||||
<string name="dav_service_refresh_couldnt_refresh">No se pudo refrescar lista de colección</string>
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">Ajustes</string>
|
||||
<string name="app_settings_user_interface">Interfaz de usuario</string>
|
||||
<string name="app_settings_reset_hints">Restablecer advertencias</string>
|
||||
<string name="app_settings_reset_hints_summary">Habilita las advertencias que han sido cesadas con anterioridad</string>
|
||||
<string name="app_settings_reset_hints_success">Todas las advertencias serán mostradas de nuevo</string>
|
||||
<string name="app_settings_security">Seguridad</string>
|
||||
<string name="app_settings_reset_trusted_certificates">Restablecer certificados de confianza</string>
|
||||
<string name="app_settings_reset_trusted_certificates_summary">Olvidar todos los certificados aceptados previamente</string>
|
||||
<plurals name="app_settings_reset_trusted_certificates_success">
|
||||
<item quantity="one">Retirada la confianza de un certificado</item>
|
||||
<item quantity="other">Retirada la confianza de %d certificados</item>
|
||||
</plurals>
|
||||
<string name="app_settings_debug">Depuración</string>
|
||||
<string name="app_settings_log_to_external_storage">Registrar en fichero externo</string>
|
||||
<string name="app_settings_log_to_external_storage_on">Registro en almacenamiento externo (si está disponible)</string>
|
||||
<string name="app_settings_log_to_external_storage_off">El archivo de registro externo está deshabilitado</string>
|
||||
<string name="app_settings_show_debug_info">Mostrar la información de depuración</string>
|
||||
<string name="app_settings_show_debug_info_details">Ver/compartir detalles de software y configuración</string>
|
||||
<!--AccountActivity-->
|
||||
<string name="account_synchronize_now">Sincronizar ahora</string>
|
||||
<string name="account_synchronizing_now">Sincronizando...</string>
|
||||
@@ -56,6 +77,17 @@
|
||||
<string name="account_create_new_address_book">Crear nueva lista de contactos</string>
|
||||
<string name="account_refresh_calendar_list">Refrescar calendario</string>
|
||||
<string name="account_create_new_calendar">Crear nuevo calendario</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">Permisos de DAVdroid</string>
|
||||
<string name="permissions_calendar">Permisos de calendario</string>
|
||||
<string name="permissions_calendar_details">Para sincronizar eventos CalDAV con sus calendarios locales, DAVdroid necesita acceder a los mismos.</string>
|
||||
<string name="permissions_calendar_request">Solicitar permisos sobre calendario</string>
|
||||
<string name="permissions_contacts">Permisos de contactos</string>
|
||||
<string name="permissions_contacts_details">Para sincronizar libretas de contactos CadDAV con sus contactos locales, DAVdroid necesita acceder a los mismos.</string>
|
||||
<string name="permissions_contacts_request">Solicitar permisos sobre contactos</string>
|
||||
<string name="permissions_opentasks">Permisos de OpenTasks</string>
|
||||
<string name="permissions_opentasks_details">Para sincronizar listas de tareas CalDAV con sus listas de tareas locales, DAVdroid necesita acceder a OpenTasks.</string>
|
||||
<string name="permissions_opentasks_request">Solicitar permisos sobre OpenTasks</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">Añadir cuenta</string>
|
||||
<string name="login_type_email">Acceder con cuenta de correo</string>
|
||||
@@ -119,6 +151,14 @@
|
||||
<item>Cada 4 horas</item>
|
||||
<item>Una vez al día</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">Sincronizar sólo sobre WiFi</string>
|
||||
<string name="settings_sync_wifi_only_on">La sincronización está restringida a conexiones WiFi</string>
|
||||
<string name="settings_sync_wifi_only_off">Tipo de conexión no tomada en consideración</string>
|
||||
<string name="settings_sync_wifi_only_ssid">Restricción WiFi SSID</string>
|
||||
<string name="settings_sync_wifi_only_ssid_on">Sólo se sincronizará sobre %s</string>
|
||||
<string name="settings_sync_wifi_only_ssid_off">Todas las conexiones WiFi pueden ser usadas</string>
|
||||
<string name="settings_sync_wifi_only_ssid_message">Introduzca el nombre de una red WiFi (SSID) para restringir la sincronización a esta red, o deje el campo en blanco para usar todas las conexiones WiFi.</string>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Límite de tiempo de eventos pasados</string>
|
||||
<string name="settings_sync_time_range_past_none">Todos los eventos serán sincronizados</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
@@ -126,6 +166,9 @@
|
||||
<item quantity="other">Los eventos anteriores a %d días serán ignorados</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Los eventos anteriores a este número de días serán ignorados (puede ser 0). Deje en blanco el campo para sincronizar todos los eventos.</string>
|
||||
<string name="settings_manage_calendar_colors">Colores de calendario</string>
|
||||
<string name="settings_manage_calendar_colors_on">Los colores de los calendarios son administrados por DAVdroid</string>
|
||||
<string name="settings_manage_calendar_colors_off">Los colores de los calendarios no son establecidos por DAVdroid</string>
|
||||
<string name="settings_version_update">Actualización de la versión de Android</string>
|
||||
<string name="settings_version_update_settings_updated">Los ajustes internos han sido actualizados.</string>
|
||||
<string name="settings_version_update_install_hint">¿Problemas? Desinstala y vuelve a instalar DAVdroid.</string>
|
||||
@@ -144,6 +187,7 @@
|
||||
<string name="create_collection_display_name">Nombre mostrado (título):</string>
|
||||
<string name="create_collection_display_name_required">Título requerido</string>
|
||||
<string name="create_collection_description">Descripción (opcional):</string>
|
||||
<string name="create_collection_home_set">Establecer localización:</string>
|
||||
<string name="create_collection_create">Crear</string>
|
||||
<string name="delete_collection">Eliminar colección</string>
|
||||
<string name="delete_collection_confirm_title">¿Está seguro/a?</string>
|
||||
@@ -156,24 +200,13 @@
|
||||
<string name="exception_show_details">Mostrar detalles</string>
|
||||
<!--sync errors and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Información de depuración</string>
|
||||
<string name="sync_error_permissions">Permisos de DAVdroid</string>
|
||||
<string name="sync_error_permissions_text">Permisos adicionales requeridos</string>
|
||||
<string name="sync_error_calendar">La sincronización de calendario falló (%s)</string>
|
||||
<string name="sync_error_contacts">La sincronización de agenda falló (%s)</string>
|
||||
<string name="sync_error_tasks">La sincronización de tareas falló (%s)</string>
|
||||
<string name="sync_error">Error al %s</string>
|
||||
<string name="sync_error_http_dav">Error de servidor al %s</string>
|
||||
<string name="sync_error_local_storage">Error de base de datos al %s</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>preparar sincronización</item>
|
||||
<item>consultar capacidades</item>
|
||||
<item>procesar entradas borradas localmente</item>
|
||||
<item>preparar entradas creadas/modificadas</item>
|
||||
<item>cargar entradas creadas/modificadas</item>
|
||||
<item>comprobar estado de sincronización</item>
|
||||
<item>listar entradas locales</item>
|
||||
<item>listar entradas remotas</item>
|
||||
<item>comparar entradas locaesl/remotas</item>
|
||||
<item>descargar entradas remotas</item>
|
||||
<item>guardar estado de sincronización</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">Nombre de usuario/contraseña erróneo</string>
|
||||
</resources>
|
||||
|
||||
@@ -3,19 +3,117 @@
|
||||
<!--common strings-->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="help">Aide</string>
|
||||
<string name="manage_accounts">Gestion des comptes</string>
|
||||
<string name="please_wait">SVP attendez ...</string>
|
||||
<string name="send">Envoyer</string>
|
||||
<!--startup dialogs-->
|
||||
<string name="startup_dont_show_again">Ne plus afficher</string>
|
||||
<string name="startup_development_version">Pré-version de DAVdroid</string>
|
||||
<string name="startup_development_version_message">Il s\'agit d\'une version de développement de DAVdroid. Il se peut que les choses ne fonctionnent pas comme prévu. S’il vous plaît faites-nous un retour pour améliorer DAVdroid.</string>
|
||||
<string name="startup_development_version_give_feedback">Faire un commentaire</string>
|
||||
<string name="startup_donate">Open-Source Information</string>
|
||||
<string name="startup_donate_message">Nous sommes heureux que vous utilisez DAVdroid, qui est un logiciel open-source (GPLv3). Parce que développer DAVdroid est un travail difficile et nous a pris de nombreuses heures, s\'il vous plaît envisager de faire un don.</string>
|
||||
<string name="startup_donate_now">Faire un don</string>
|
||||
<string name="startup_donate_later">Plus tard</string>
|
||||
<string name="startup_google_play_accounts_removed">Erreur information Play Store DRM</string>
|
||||
<string name="startup_google_play_accounts_removed_message">Dans certaines conditions, Play Store DRM peut provoquer la disparition de tous les comptes DAVdroid après un redémarrage ou après la mise à niveau de DAVdroid. Si vous êtes concerné par ce problème (et seulement alors), s\'il vous plaît installer \"DAVdroid JB Solution\" du Play Store.</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Plus d\'information</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks n\'est pas installé</string>
|
||||
<string name="startup_opentasks_not_installed_message">L\'application OpenTasks n\'est pas disponible, donc DAVdroid ne pourra pas synchroniser des listes de tâches.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Après l\'installation OpenTasks, vous devez RE-INSTALLER DAVdroid et ajoutez vos comptes à nouveau (bug Android).</string>
|
||||
<string name="startup_opentasks_not_installed_install">Installer OpenTasks</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Conditions d\'utilisation</string>
|
||||
<string name="about_license_info_no_warranty">Ce programme est fourni sans AUCUNE GARANTIE. C\'est un logiciel libre, et vous êtes en droit de le redistribuer sous certaines conditions.</string>
|
||||
<!--global settings-->
|
||||
<string name="logging_davdroid_file_logging">DAVdroid fichier de journalisation</string>
|
||||
<string name="logging_to_external_storage">Se connecter au stockage externe: %s</string>
|
||||
<string name="logging_to_external_storage_warning">Supprimer les journaux dès que possible!</string>
|
||||
<string name="logging_couldnt_create_file">Impossible de créer le fichier journal externe: %s</string>
|
||||
<string name="logging_no_external_storage">Stockage externe introuvable</string>
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_open">Ouvrir le tiroir de navigation</string>
|
||||
<string name="navigation_drawer_close">Fermer le tiroir de navigation</string>
|
||||
<string name="navigation_drawer_about">A propos / Licence</string>
|
||||
<string name="navigation_drawer_settings">Paramètres</string>
|
||||
<string name="navigation_drawer_news_updates">Actualité & mise à jour</string>
|
||||
<string name="navigation_drawer_external_links">Liens externes</string>
|
||||
<string name="navigation_drawer_website">Site Web</string>
|
||||
<string name="navigation_drawer_faq">FAQ</string>
|
||||
<string name="navigation_drawer_forums">Communauté</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>
|
||||
<!--DavService-->
|
||||
<string name="dav_service_refresh_failed">La détection du service a échoué</string>
|
||||
<string name="dav_service_refresh_couldnt_refresh">Impossible d\'actualiser la liste de collection</string>
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">Paramètres</string>
|
||||
<string name="app_settings_user_interface">Interface utilisateur</string>
|
||||
<string name="app_settings_reset_hints">Réinitialiser les astuces</string>
|
||||
<string name="app_settings_reset_hints_summary">Réactiver les astuces qui ont été vu précédemment</string>
|
||||
<string name="app_settings_reset_hints_success">Toutes les astuces seront affichés à nouveau</string>
|
||||
<string name="app_settings_security">Sécurité</string>
|
||||
<string name="app_settings_reset_trusted_certificates">Réinitialiser les certificats de confiance</string>
|
||||
<string name="app_settings_reset_trusted_certificates_summary">Oublier tous les certificats qui ont été acceptés précédemment</string>
|
||||
<plurals name="app_settings_reset_trusted_certificates_success">
|
||||
<item quantity="one">Un certificat douteux</item>
|
||||
<item quantity="other">%d certificats douteux</item>
|
||||
</plurals>
|
||||
<string name="app_settings_debug">Débogage</string>
|
||||
<string name="app_settings_log_to_external_storage">Journaliser dans un fichier externe</string>
|
||||
<string name="app_settings_log_to_external_storage_on">Journaliser sur le stockage externe (si disponible)</string>
|
||||
<string name="app_settings_log_to_external_storage_off">Le fichier externe n\'est pas disponible.</string>
|
||||
<string name="app_settings_show_debug_info">Afficher les infos de débogage</string>
|
||||
<string name="app_settings_show_debug_info_details">Voir/partager l\'application et les détails de configuration</string>
|
||||
<!--AccountActivity-->
|
||||
<string name="account_synchronize_now">Synchroniser maintenant</string>
|
||||
<string name="account_synchronizing_now">Synchronisation en cours</string>
|
||||
<string name="account_settings">Paramètres du compte</string>
|
||||
<string name="account_delete">Supprimer le compte</string>
|
||||
<string name="account_delete_confirmation_title">Voulez-vous vraiment supprimer le compte?</string>
|
||||
<string name="account_delete_confirmation_text">Toutes les copies locales des carnets d\'adresses, des calendriers et des listes de tâches seront supprimées.</string>
|
||||
<string name="account_refresh_address_book_list">Actualiser le carnet d\'adresse</string>
|
||||
<string name="account_create_new_address_book">Créer un nouveau carnet d\'adresse</string>
|
||||
<string name="account_refresh_calendar_list">Actualiser le calendrier</string>
|
||||
<string name="account_create_new_calendar">Créer un nouveau calendrier</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">Autorisations DAVdroid</string>
|
||||
<string name="permissions_calendar">Autorisations calendrier</string>
|
||||
<string name="permissions_calendar_details">Pour synchroniser les événements CalDAV avec vos calendriers locaux, DAVdroid a besoin d\'accéder à vos calendriers.</string>
|
||||
<string name="permissions_calendar_request">Demande d\'autorisations d\'accéder au calendrier</string>
|
||||
<string name="permissions_contacts">Autorisations contacts</string>
|
||||
<string name="permissions_contacts_details">Pour synchroniser les carnets d\'adresses de CardDAV avec votre carnet d\'adresses local, DAVdroid a besoin d\'accéder à vos contacts.</string>
|
||||
<string name="permissions_contacts_request">Demande d\'autorisations d\'accéder aux contacts</string>
|
||||
<string name="permissions_opentasks">Autorisations OpenTasks</string>
|
||||
<string name="permissions_opentasks_details">Pour synchroniser les tâches de CalDAV avec vos listes de tâches locales, DAVdroid a besoin d\'accéder à OpenTasks.</string>
|
||||
<string name="permissions_opentasks_request">Demande d\'autorisations d\'accéder à OpenTasks</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">Ajouter un compte</string>
|
||||
<string name="login_type_email">Connexion avec une adresse email</string>
|
||||
<string name="login_email_address">Adresse mail</string>
|
||||
<string name="login_email_address_error">Une adresse e-mail valide est requis</string>
|
||||
<string name="login_password">Mot de passe</string>
|
||||
<string name="login_password_required">Mot de passe requis</string>
|
||||
<string name="login_type_url">Connexion avec une URL et un nom d\'utilisateur</string>
|
||||
<string name="login_url_must_be_http_or_https">L\'URL doit commencer par http(s)://</string>
|
||||
<string name="login_url_host_name_required">Nom d\'hôte requis</string>
|
||||
<string name="login_user_name">Nom d\'utilisateur</string>
|
||||
<string name="login_user_name_required">Nom d\'utilisateur requis</string>
|
||||
<string name="login_base_url">URL de base</string>
|
||||
<string name="login_auth_preemptive">Authentification préventive (recommandé, mais incompatible avec l\'authentification Digest)</string>
|
||||
<string name="login_login">Se connecter</string>
|
||||
<string name="login_back">Retour</string>
|
||||
<string name="login_create_account">Créer un compte</string>
|
||||
<string name="login_account_name">Nom du compte</string>
|
||||
<string name="login_account_name_info">Utilisez votre adresse e-mail comme nom de compte car Android utilisera ce nom en tant que champ ORGANISATEUR pour les événements que vous créerez. Vous ne pouvez pas avoir deux comptes avec le même nom.</string>
|
||||
<string name="login_account_name_required">Nom du compte requis</string>
|
||||
<string name="login_account_not_created">Le compte n\'a pas pu être créé</string>
|
||||
<string name="login_configuration_detection">Détection de la configuration</string>
|
||||
<string name="login_querying_server">S\'il vous plaît patienter, nous interrogeons le serveur ...</string>
|
||||
<string name="login_no_caldav_carddav">Aucun service CalDAV ou CardDAV trouvé.</string>
|
||||
<string name="login_view_logs">Voir infos de débogage</string>
|
||||
<!--AccountSettingsActivity-->
|
||||
<string name="settings_title">Paramètres: %s</string>
|
||||
<string name="settings_authentication">Authentification</string>
|
||||
<string name="settings_username">Nom d\'utilisateur</string>
|
||||
<string name="settings_enter_username">Saisissez votre nom d\'utilisateur :</string>
|
||||
@@ -27,7 +125,7 @@
|
||||
<string name="settings_preemptive_off">Le nom d\'utilisateur et le mot de passe sont envoyés à la demande du serveur.</string>
|
||||
<string name="settings_sync">Synchronisation</string>
|
||||
<string name="settings_sync_interval_contacts">Interval de synchronisation des carnets d\'adresses</string>
|
||||
<string name="settings_sync_summary_manually">A la demande</string>
|
||||
<string name="settings_sync_summary_manually">Manuellement</string>
|
||||
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">Toutes les %d minutes et immédiatement après un changement local</string>
|
||||
<string name="settings_sync_summary_not_available">Indisponible</string>
|
||||
<string name="settings_sync_interval_calendars">Interval de synchronisation des agendas</string>
|
||||
@@ -52,28 +150,70 @@
|
||||
<item>Toutes les 4 heures</item>
|
||||
<item>Une fois par jour</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">Synchronisation en Wifi seulement</string>
|
||||
<string name="settings_sync_wifi_only_on">La synchronisation est limitée aux connexions WiFi</string>
|
||||
<string name="settings_sync_wifi_only_off">Le type de connexion n\'est pas pris en charge</string>
|
||||
<string name="settings_sync_wifi_only_ssid">Restriction WiFi SSID</string>
|
||||
<string name="settings_sync_wifi_only_ssid_on">Sera seulement synchroniser avec %s</string>
|
||||
<string name="settings_sync_wifi_only_ssid_off">Toutes les connexions WiFi peuvent être utilisées</string>
|
||||
<string name="settings_sync_wifi_only_ssid_message">Entrez le nom d\'un réseau WiFi (SSID) pour restreindre la synchronisation à ce réseau, ou laisser vide pour autoriser toutes les connexions WiFi.</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string name="settings_rfc6868_for_vcards">Utiliser RFC6868 pour VCards</string>
|
||||
<string name="settings_rfc6868_for_vcards_on">Les guillemets doubles peuvent être utilisés dans les valeurs de paramètre</string>
|
||||
<string name="settings_rfc6868_for_vcards_off">Les guillemets doubles ne peuvent pas être utilisés dans les valeurs de paramètre</string>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Limite des événements passés</string>
|
||||
<string name="settings_sync_time_range_past_none">Tous les événements seront synchronisés</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="one">Les événements de plus d’un jour passé seront ignorés</item>
|
||||
<item quantity="other">Les événements de plus de %d jours passés seront ignorés</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Les événements antérieurs à ce nombre de jours seront ignorés (peut être 0). Laissez vide pour synchroniser tous les événements.</string>
|
||||
<string name="settings_manage_calendar_colors">Choisir couleur du calendrier</string>
|
||||
<string name="settings_manage_calendar_colors_on">Les couleurs de calendrier sont gérées par DAVdroid</string>
|
||||
<string name="settings_manage_calendar_colors_off">Les couleurs de calendrier ne sont pas gérées par DAVdroid</string>
|
||||
<string name="settings_version_update">Mise à jour de la version de DAVdroid</string>
|
||||
<string name="settings_version_update_settings_updated">Les paramètres internes ont été mis à jour.</string>
|
||||
<string name="settings_version_update_install_hint">Un problème? Désinstaller DAVdroid, puis réinstaller.</string>
|
||||
<!--collection management-->
|
||||
<string name="create_addressbook">Créer un carnet d\'adresses</string>
|
||||
<string name="create_addressbook_display_name_hint">Mon carnet d\'adresses</string>
|
||||
<string name="create_calendar">Créer une collection CalDAV</string>
|
||||
<string name="create_calendar_display_name_hint">Mon calendrier</string>
|
||||
<string name="create_calendar_time_zone">Fuseau horaire:</string>
|
||||
<string name="create_calendar_type">Type de collection:</string>
|
||||
<string name="create_calendar_type_only_events">Calendrier (événements seulement)</string>
|
||||
<string name="create_calendar_type_only_tasks">Liste de tâches (tâches seulement)</string>
|
||||
<string name="create_calendar_type_events_and_tasks">Fusionner (événements et tâches)</string>
|
||||
<string name="create_collection_color">Choisir une couleur pour la collection</string>
|
||||
<string name="create_collection_creating">Création collection</string>
|
||||
<string name="create_collection_display_name">Le nom affiché (titre) pour cette collection:</string>
|
||||
<string name="create_collection_display_name_required">Titre requis</string>
|
||||
<string name="create_collection_description">Description (facultatif)</string>
|
||||
<string name="create_collection_home_set">Accueil:</string>
|
||||
<string name="create_collection_create">Créer</string>
|
||||
<string name="delete_collection">Supprimer la collection</string>
|
||||
<string name="delete_collection_confirm_title">Êtes-vous sur?</string>
|
||||
<string name="delete_collection_confirm_warning">Cette collection (%s) et toutes ses données seront supprimées du serveur.</string>
|
||||
<string name="delete_collection_deleting_collection">Suppression de la collection</string>
|
||||
<!--ExceptionInfoFragment-->
|
||||
<string name="exception">Une erreur est survenue.</string>
|
||||
<string name="exception_httpexception">Une erreur HTTP est survenue.</string>
|
||||
<string name="exception_ioexception">Une erreur I/O est survenue.</string>
|
||||
<string name="exception_show_details">Voir détails</string>
|
||||
<!--sync errors and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Infos de débogage</string>
|
||||
<string name="sync_error_permissions">Autorisations DAVdroid</string>
|
||||
<string name="sync_error_permissions_text">Autorisations supplémentaires demandées</string>
|
||||
<string name="sync_error_calendar">Échec de la synchronisation du calendrier (%s)</string>
|
||||
<string name="sync_error_contacts">Échec de la synchronisation du carnet d\'adresse (%s)</string>
|
||||
<string name="sync_error_tasks">Échec de la synchronisation (%s)</string>
|
||||
<string name="sync_error">Erreur durant %s</string>
|
||||
<string name="sync_error_http_dav">Erreur de serveur durant %s</string>
|
||||
<string name="sync_error_local_storage">Erreur de base de donnée durant %s</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>prépare la synchronisation</item>
|
||||
<item>demande les autorisations</item>
|
||||
<item>prépare la suppression des entrées locales</item>
|
||||
<item>prépare les entrées créées/modifiées</item>
|
||||
<item>envoi les entrées créées/modifiées</item>
|
||||
<item>vérifie l\'état de la synchronisation</item>
|
||||
<item>liste les entrées locales</item>
|
||||
<item>liste les entrées distantes</item>
|
||||
<item>compare les entrées locales/distantes</item>
|
||||
<item>télécharge les entrées distantes</item>
|
||||
<item>enregistre l\'état de la synchronisation</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">Nom d\'utilisateur ou mot de passe erroné</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
<string name="startup_google_play_accounts_removed_message">Bizonyos körülmények között a Play Áruház DRM okozhatja azt, hogy az eszköz újraindítását vagy a DAVdroid frissítését követően a DAVdroid fiókok eltűnnek. Amennyiben (és csak amennyiben) érinti Önt ez a probléma, telepítse a \"DAVdroid JB Workaround\" alkalmazást Play Áruházból.</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">További információk</string>
|
||||
<string name="startup_opentasks_not_installed">Az OpenTasks nincs telepítve</string>
|
||||
<string name="startup_opentasks_not_installed_message">Az OpenTasks alkalmazás nincs telepítve vagy nem elérhető, ezért a DAVdroid nem lesz képes a feladatlisták szinkronizálására. Az OpenTasks telepítése után telepítse újra a DAVdroid-ot majd hozza létre a fiókot újra.</string>
|
||||
<string name="startup_opentasks_not_installed_message">Az OpenTasks alkalmazás nincs telepítve, így a DAVdroid nem lesz képes szinkronizálni feladatlistákat.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Az OpenTasks telepítését követően újra kell telepíteni a DAVdroit alkalmazást és újra fel kell venni a fiókokat (Android hiba).</string>
|
||||
<string name="startup_opentasks_not_installed_install">Az OpenTasks telepítése</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Licencfeltételek</string>
|
||||
@@ -76,6 +77,17 @@
|
||||
<string name="account_create_new_address_book">Új címjegyzék létrehozása</string>
|
||||
<string name="account_refresh_calendar_list">Naptárlista frissítése</string>
|
||||
<string name="account_create_new_calendar">Új naptár létrehozása</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">DAVdroid engedélyek </string>
|
||||
<string name="permissions_calendar">Naptárengedély</string>
|
||||
<string name="permissions_calendar_details">A CalDAV naptárak és a helyi naptárak szinkronizálásához a DAVdroid naptárhozzáférést igényel.</string>
|
||||
<string name="permissions_calendar_request">Naptárhozzáférés igénylése</string>
|
||||
<string name="permissions_contacts">Névjegyengedélyek</string>
|
||||
<string name="permissions_contacts_details">A CardDAV címlisták és a helyi címlisták szinkronizálásához a névjegyhozzáférést igényel.</string>
|
||||
<string name="permissions_contacts_request">Névjegyengedélyek igénylése</string>
|
||||
<string name="permissions_opentasks">OpenTasks engedélyek</string>
|
||||
<string name="permissions_opentasks_details">A CalDAV feladatlisták és a helyi feladatlisták szinkronizálásához a DAVdroid OpenTasks hozzáférést igényel.</string>
|
||||
<string name="permissions_opentasks_request">OpenTasks engedélyek igénylése</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">Fiók hozzáadása</string>
|
||||
<string name="login_type_email">Bejelentkezés email cím segítségével</string>
|
||||
@@ -95,6 +107,7 @@
|
||||
<string name="login_create_account">Fiók létrehozása</string>
|
||||
<string name="login_account_name">A fiók neve</string>
|
||||
<string name="login_account_name_info">Használja az email címet fióknévként, mert később a létrehozandó események szervezőjeként (ORGANIZER mező) az Android ezt fogja használni. Két fiókot nem lehet azonos néven létrehozni.</string>
|
||||
<string name="login_account_contact_group_method">A csoportok kezelésének módja:</string>
|
||||
<string name="login_account_name_required">A fióknév megadása feltétlenül szükséges</string>
|
||||
<string name="login_account_not_created">A fiók létrehozása nem sikerült</string>
|
||||
<string name="login_configuration_detection">A konfiguráció felderítése</string>
|
||||
@@ -139,6 +152,23 @@
|
||||
<item>4 óránként</item>
|
||||
<item>Naponta</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">Szinkronizálás csak WIFI-n</string>
|
||||
<string name="settings_sync_wifi_only_on">Csak WIFI kapcsolat keresztül történjen szinkronizálás</string>
|
||||
<string name="settings_sync_wifi_only_off">Szinkronizálás a kapcsolat típusától függetlenül</string>
|
||||
<string name="settings_sync_wifi_only_ssid">WIFI SSID szűkítés</string>
|
||||
<string name="settings_sync_wifi_only_ssid_on">Szinkronizálás csak a(z) %s hálózatra kapcsolódva</string>
|
||||
<string name="settings_sync_wifi_only_ssid_off">Szinkronizálás bármely WIFI hálózaton</string>
|
||||
<string name="settings_sync_wifi_only_ssid_message">Adja meg a WIFI hálózat nevét (SSID) a szinkronizálás egy hálózatra való korlátozához, vagy hagyja üresen, ha nem akar ilyen szűkítést.</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">A csoportok kezelésének módja</string>
|
||||
<string-array name="settings_contact_group_method_entries">
|
||||
<item>Minden csoport egy különálló VCard</item>
|
||||
<item>A csoportok a kapcsolatonkéni kategóriák</item>
|
||||
</string-array>
|
||||
<string name="settings_rfc6868_for_vcards">RFC6868 szabványnak megfelelő VCards használata</string>
|
||||
<string name="settings_rfc6868_for_vcards_on">A paraméterértékek tartalmazhatnak idézőjelet</string>
|
||||
<string name="settings_rfc6868_for_vcards_off">A paraméterértékek nem tartalmazhatnak idézőjelet</string>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Múltbéli események időkorlátja</string>
|
||||
<string name="settings_sync_time_range_past_none">Minden esemény szinkronizálása</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
@@ -146,6 +176,9 @@
|
||||
<item quantity="other">A %d napnál régebbi események figyelmen kívül hagyása</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Az ennyi napnál (lehet 0) régebbi események figyelmen kívül lesznek hagyva. Hagyja üresen, ha minden múltbéli eseményt szinkronizálni akar.</string>
|
||||
<string name="settings_manage_calendar_colors">Naptárszínek kezelése</string>
|
||||
<string name="settings_manage_calendar_colors_on">A naptárszíneket a DAVdroid kezeli</string>
|
||||
<string name="settings_manage_calendar_colors_off">A naptárszíneket nem a DAVdroid kezeli</string>
|
||||
<string name="settings_version_update">DAVdroid frissítése</string>
|
||||
<string name="settings_version_update_settings_updated">A belső beállítások frissítve lettek.</string>
|
||||
<string name="settings_version_update_install_hint">Probléma? Próbálja meg törölni majd újratelepíteni a DAVdroid-ot.</string>
|
||||
@@ -177,6 +210,8 @@
|
||||
<string name="exception_show_details">Részletek megjelenítése</string>
|
||||
<!--sync errors and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Hibakeresési információ</string>
|
||||
<string name="sync_error_permissions">DAVdroid engedélyek </string>
|
||||
<string name="sync_error_permissions_text">További engedélyek szükségesek</string>
|
||||
<string name="sync_error_calendar">A naptár szinkronizálása nem sikerült (%s)</string>
|
||||
<string name="sync_error_contacts">A címjegyzék szinkronizálása nem sikerült (%s)</string>
|
||||
<string name="sync_error_tasks">A feladatok szinkronizálása nem sikerült (%s)</string>
|
||||
@@ -184,16 +219,17 @@
|
||||
<string name="sync_error_http_dav">Szerver oldali hiba az alábbi művelet közben: %s</string>
|
||||
<string name="sync_error_local_storage">Adatbázishiba az alábbi művelet közben: %s</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>felkészülés a szinkronizálásra</item>
|
||||
<item>szolgáltatások keresése</item>
|
||||
<item>szinkronizáció előkészítése </item>
|
||||
<item>szerver képességeinek lekérdezése</item>
|
||||
<item>a helyben törölt bejegyzések feldolgozása</item>
|
||||
<item>az új vagy módosított bejegyzések összegyűjtése</item>
|
||||
<item>az új vagy módosított bejegyzések gyűjtése </item>
|
||||
<item>az új vagy módosított bejegyzések feltöltése</item>
|
||||
<item>a szinkronizációs állapot ellenőrzése</item>
|
||||
<item>szinkronizációs állapot ellenőrzése</item>
|
||||
<item>helyi bejegyzések listázása</item>
|
||||
<item>távoli bejegyzések listázása</item>
|
||||
<item>helyi és távoli bejegyzések összehasonlítása</item>
|
||||
<item>távoli bejegyzések letöltése</item>
|
||||
<item>utófeldolgozás</item>
|
||||
<item>szinkronizációs állapot mentése</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">A felhasználónév vagy jelszó hibás</string>
|
||||
|
||||
84
app/src/main/res/values-it/strings.xml
Normal file
84
app/src/main/res/values-it/strings.xml
Normal file
@@ -0,0 +1,84 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!--common strings-->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="help">Aiuto</string>
|
||||
<string name="manage_accounts">Gestione account</string>
|
||||
<string name="please_wait">Attendere prego …</string>
|
||||
<!--startup dialogs-->
|
||||
<string name="startup_dont_show_again">Non mostrare più</string>
|
||||
<string name="startup_donate_now">Mostra pagina donazioni</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Maggiori informazioni</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks non installata</string>
|
||||
<string name="startup_opentasks_not_installed_install">Installa OpenTasks</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Termini di licenza</string>
|
||||
<!--global settings-->
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_about">Informazioni / Licenza</string>
|
||||
<string name="navigation_drawer_settings">Impostazioni</string>
|
||||
<string name="navigation_drawer_news_updates">Notizie & aggiornamenti</string>
|
||||
<string name="navigation_drawer_external_links">Link esterni</string>
|
||||
<string name="navigation_drawer_website">Sito web</string>
|
||||
<string name="navigation_drawer_faq">Domande Frequenti</string>
|
||||
<string name="navigation_drawer_forums">Comunità</string>
|
||||
<string name="navigation_drawer_donate">Donazione</string>
|
||||
<!--DavService-->
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">Impostazioni</string>
|
||||
<string name="app_settings_user_interface">Interfaccia utente</string>
|
||||
<string name="app_settings_security">Sicurezza</string>
|
||||
<!--AccountActivity-->
|
||||
<string name="account_synchronize_now">Sincronizza adesso</string>
|
||||
<string name="account_synchronizing_now">Sincronizzazione in corso</string>
|
||||
<string name="account_settings">Impostazioni account</string>
|
||||
<string name="account_delete">Elimina account</string>
|
||||
<string name="account_delete_confirmation_text">Tutte le copie locali delle rubriche, dei calendari e degli elenchi attività verranno eliminate.</string>
|
||||
<string name="account_refresh_calendar_list">Aggiorna lista calendari</string>
|
||||
<string name="account_create_new_calendar">Crea nuovo calendario</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">Permessi DAVdroid</string>
|
||||
<string name="permissions_calendar">Permessi Calendario</string>
|
||||
<string name="permissions_contacts">Permessi Contatti</string>
|
||||
<string name="permissions_opentasks">Permessi OpenTasks</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">Aggiungi account</string>
|
||||
<string name="login_type_email">Accedi con indirizzo email</string>
|
||||
<string name="login_email_address">Indirizzo email</string>
|
||||
<string name="login_password">Password</string>
|
||||
<string name="login_type_url">Accedi con URL e nome utente</string>
|
||||
<string name="login_url_must_be_http_or_https">L\'URL deve iniziare con http(s)://</string>
|
||||
<string name="login_user_name">Nome utente</string>
|
||||
<string name="login_back">Indietro</string>
|
||||
<string name="login_create_account">Crea account</string>
|
||||
<string name="login_account_name">Nome account</string>
|
||||
<string name="login_configuration_detection">Rilevazione configurazione</string>
|
||||
<!--AccountSettingsActivity-->
|
||||
<string name="settings_title">Impostazioni: %s</string>
|
||||
<string name="settings_authentication">Autenticazione</string>
|
||||
<string name="settings_username">Nome utente</string>
|
||||
<string name="settings_enter_username">Inserisci nome utente:</string>
|
||||
<string name="settings_password">Password</string>
|
||||
<string name="settings_enter_password">Inserisci la tua password:</string>
|
||||
<string name="settings_sync_interval_contacts">Intervallo sincr. Contatti</string>
|
||||
<string name="settings_sync_summary_manually">Solo manualmente</string>
|
||||
<string name="settings_sync_interval_calendars">Intervallo sincr. Calendari</string>
|
||||
<string name="settings_sync_interval_tasks">Intervallo sincr. Attività</string>
|
||||
<string name="settings_sync_wifi_only">Sincr. solo tramite WiFi</string>
|
||||
<string name="settings_sync_wifi_only_on">La sincronizzazione è limitata alle connessioni WiFi</string>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past_none">Verranno sincronizzati tutti gli eventi</string>
|
||||
<string name="settings_version_update_install_hint">Problemi? Disinstalla e poi re-installa DAVdroid.</string>
|
||||
<!--collection management-->
|
||||
<string name="create_calendar_time_zone">Fuso orario:</string>
|
||||
<string name="create_calendar_type_only_events">Calendario (solo eventi)</string>
|
||||
<string name="create_calendar_type_only_tasks">Elenco attività (solo attività)</string>
|
||||
<string name="delete_collection_confirm_title">Sei sicuro?</string>
|
||||
<!--ExceptionInfoFragment-->
|
||||
<string name="exception">Si è verificato un errore.</string>
|
||||
<string name="exception_httpexception">Si è verificato un errore HTTP.</string>
|
||||
<string name="exception_ioexception">Si è verificato un errore di I/O.</string>
|
||||
<string name="exception_show_details">Mostra dettagli</string>
|
||||
<!--sync errors and DebugInfoActivity-->
|
||||
<string name="sync_error_permissions">Permessi DAVdroid</string>
|
||||
</resources>
|
||||
@@ -19,7 +19,8 @@
|
||||
<string name="startup_google_play_accounts_removed_message">特定の条件下で、DAVdroid を再起動後またはアップグレードした後、Play ストア DRM によりすべての DAVdroid アカウントがなくなる可能性があります。この問題の影響を受けている場合 (のみ)、Play ストアから「DAVdroid JB 回避策」をインストールしてください。</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">追加情報</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks がインストールされていません</string>
|
||||
<string name="startup_opentasks_not_installed_message">OpenTasks アプリがインストールされていないか、アクセスできないため、DAVdroid は、タスクリストを同期することができません。 OpenTasks をインストールした後、DAVdroidを再インストールして、再度アカウントを追加してください。</string>
|
||||
<string name="startup_opentasks_not_installed_message">OpenTasks アプリが利用できないため、DAVdroid はタスクリストを同期することができません。</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">OpenTasks をインストールした後で、DAVdroidを再インストールして、再度アカウントを追加してください (Android のバグ)。</string>
|
||||
<string name="startup_opentasks_not_installed_install">OpenTasks をインストール</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">ライセンス規約</string>
|
||||
@@ -75,6 +76,17 @@
|
||||
<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">ローカルの連絡先と CalDAV アドレス帳を同期するため、DAVdroid が連絡先にアクセスする必要があります。</string>
|
||||
<string name="permissions_contacts_request">連絡先アクセス許可の要求</string>
|
||||
<string name="permissions_opentasks">OpenTasks アクセス許可</string>
|
||||
<string name="permissions_opentasks_details">ローカルのタスクリストと CalDAV タスクを同期するため、DAVdroid が OpenTasks にアクセスする必要があります。</string>
|
||||
<string name="permissions_opentasks_request">OpenTasks アクセス許可の要求</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">アカウントを追加</string>
|
||||
<string name="login_type_email">メールアドレスでログイン</string>
|
||||
@@ -94,6 +106,7 @@
|
||||
<string name="login_create_account">アカウントを作成</string>
|
||||
<string name="login_account_name">アカウント名</string>
|
||||
<string name="login_account_name_info">Android はあなたが作成した予定の ORGANIZER フィールドとしてアカウント名を使用するので、アカウント名としてメールアドレスを使用してください。同じ名前のアカウントを 2 つ持つことはできません。</string>
|
||||
<string name="login_account_contact_group_method">連絡先グループ方法:</string>
|
||||
<string name="login_account_name_required">アカウント名が必要です</string>
|
||||
<string name="login_account_not_created">アカウントを作成できません</string>
|
||||
<string name="login_configuration_detection">設定の検出</string>
|
||||
@@ -138,12 +151,36 @@
|
||||
<item>4 時間ごと</item>
|
||||
<item>1 日 1 回</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">WiFi でのみ同期</string>
|
||||
<string name="settings_sync_wifi_only_on">同期は WiFi 接続に制限されます</string>
|
||||
<string name="settings_sync_wifi_only_off">接続の種類は考慮されません</string>
|
||||
<string name="settings_sync_wifi_only_ssid">WiFi SSID 制限</string>
|
||||
<string name="settings_sync_wifi_only_ssid_on">%s でのみ同期します</string>
|
||||
<string name="settings_sync_wifi_only_ssid_off">すべての WiFi 接続を使用することができます</string>
|
||||
<string name="settings_sync_wifi_only_ssid_message">このネットワークで同期を制限する WiFi ネットワーク (SSID) の名前を入力してください。すべての WiFi 接続は空白のままにします。</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">連絡先グループ方法</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string-array name="settings_contact_group_method_entries">
|
||||
<item>グループは個別の VCards</item>
|
||||
<item>グループは連絡先カテゴリーごと</item>
|
||||
</string-array>
|
||||
<string name="settings_rfc6868_for_vcards">RFC6868 の VCards を使用する</string>
|
||||
<string name="settings_rfc6868_for_vcards_on">二重引用符を、パラメーター値に使用することができます</string>
|
||||
<string name="settings_rfc6868_for_vcards_off">二重引用符を、パラメーター値に使用することができません</string>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">過去イベントの時間限度</string>
|
||||
<string name="settings_sync_time_range_past_none">すべてのイベントが同期されます</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="other">%d 日より前のイベントは無視されます</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">この日数より過去のイベントは無視されます (0 も可)。すべてのイベントを同期させるには、空白のままにしてください。</string>
|
||||
<string name="settings_manage_calendar_colors">カレンダーの色を管理</string>
|
||||
<string name="settings_manage_calendar_colors_on">カレンダーの色は DAVdroid が管理します</string>
|
||||
<string name="settings_manage_calendar_colors_off">カレンダーの色を DAVdroid が設定しません</string>
|
||||
<string name="settings_version_update">DAVdroid バージョンの更新</string>
|
||||
<string name="settings_version_update_settings_updated">内部設定が更新されました。</string>
|
||||
<string name="settings_version_update_install_hint">問題がありますか? DAVdroid をアンインストールして、再度インストールしてください。</string>
|
||||
@@ -175,6 +212,8 @@
|
||||
<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>
|
||||
@@ -192,6 +231,7 @@
|
||||
<item>リモートのエントリーをリスト中</item>
|
||||
<item>ローカル/リモートのエントリーを比較中</item>
|
||||
<item>リモートのエントリーをダウンロード中</item>
|
||||
<item>後処理中</item>
|
||||
<item>同期の状態を保存中</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">ユーザー名/パスワードが間違っています</string>
|
||||
|
||||
@@ -3,19 +3,118 @@
|
||||
<!--common strings-->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="help">Help</string>
|
||||
<string name="manage_accounts">Beheer accounts</string>
|
||||
<string name="please_wait">Een moment geduld...</string>
|
||||
<string name="send">Verzenden</string>
|
||||
<!--startup dialogs-->
|
||||
<string name="startup_dont_show_again">Niet opnieuw weergeven</string>
|
||||
<string name="startup_development_version">DAVdroid voorlopige versie</string>
|
||||
<string name="startup_development_version_message">Dit is een ontwikkelversie van DAVdroid. Het kan zijn dat dingen niet werken zoals verwacht. Geef ons constructieve feedback om DAVdroid te verbeteren.</string>
|
||||
<string name="startup_development_version_give_feedback">Feedback geven</string>
|
||||
<string name="startup_donate">Open-Source informatie</string>
|
||||
<string name="startup_donate_message">We zijn blij dat je DAVdroid gebruikt, wat open-source software (GPLv3) is. Omdat de ontwikkeling van DAVdroid hard werk is en duizenden uren in beslag neemt. overweeg alstublieft een donatie.</string>
|
||||
<string name="startup_donate_now">Toon donatie pagina</string>
|
||||
<string name="startup_donate_later">Misschien later</string>
|
||||
<string name="startup_google_play_accounts_removed">Play Store DRM fout-informatie</string>
|
||||
<string name="startup_google_play_accounts_removed_message">Onder bepaalde omstandigheden, kan Play Store DRM ervoor zorgen dat accounts kwijt zijn na een herstart of na een DAVdroid update. Als dit probleem zich bij je voordoet (en alleen dan), Installeer dan \"DAVdroid JB Workaround\" vanuit de Play Store</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Meer informatie</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks niet geinstalleerd</string>
|
||||
<string name="startup_opentasks_not_installed_message">De OpenTasks app is niet beschikbaar, Hierdoor is het voor DAVdroid niet mogelijk om uw taken te synchroniseren.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Na installatie van OpenTasks dient u DAVdroid opnieuw te installeren en de accounts toe te voegen (Android bug).</string>
|
||||
<string name="startup_opentasks_not_installed_install">OpenTasks installeren</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Licentie voorwaarden</string>
|
||||
<string name="about_license_info_no_warranty">Dit programma kom met ABSOLUUT GEEN GARANTIE. Het is gratis software, en je bent welkom dit te herdistribueren onder bepaalde voorwaarden.</string>
|
||||
<!--global settings-->
|
||||
<string name="logging_davdroid_file_logging">DAVDroid bestand loggen</string>
|
||||
<string name="logging_to_external_storage">Loggen naar externe opslag: %s</string>
|
||||
<string name="logging_to_external_storage_warning">Verwijder logs zo snel mogelijk</string>
|
||||
<string name="logging_couldnt_create_file">Kon extern log bestand niet verwijderen: %s</string>
|
||||
<string name="logging_no_external_storage">Externe opslag niet gevonden</string>
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_open">Open navigatie drawer</string>
|
||||
<string name="navigation_drawer_close">Sluit navigatie drawer</string>
|
||||
<string name="navigation_drawer_subtitle">CalDAV/CardDav Sync adapter</string>
|
||||
<string name="navigation_drawer_about">Over / Licentie</string>
|
||||
<string name="navigation_drawer_settings">Instellingen</string>
|
||||
<string name="navigation_drawer_news_updates">Nieuws & updates</string>
|
||||
<string name="navigation_drawer_external_links">Externe links</string>
|
||||
<string name="navigation_drawer_website">Website</string>
|
||||
<string name="navigation_drawer_faq">FAQ</string>
|
||||
<string name="navigation_drawer_forums">Community</string>
|
||||
<string name="navigation_drawer_donate">Doneren</string>
|
||||
<string name="account_list_empty">Welkom bij DAVdroid!\n\nJe kunt nu een CalDAV/CardDAv account toevoegen.</string>
|
||||
<!--DavService-->
|
||||
<string name="dav_service_refresh_failed">Service herkenning is mislukt</string>
|
||||
<string name="dav_service_refresh_couldnt_refresh">Kon de collectie lijst niet vernieuwen</string>
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">Instellingen</string>
|
||||
<string name="app_settings_user_interface">Gebruikers interface</string>
|
||||
<string name="app_settings_reset_hints">Hints resetten </string>
|
||||
<string name="app_settings_reset_hints_summary">Hints die al gezien zijn opnieuw weergeven</string>
|
||||
<string name="app_settings_reset_hints_success">Alle hints worden opnieuw weergegeven</string>
|
||||
<string name="app_settings_security">Beveiliging</string>
|
||||
<string name="app_settings_reset_trusted_certificates">Vertrouwde certificaten resetten</string>
|
||||
<string name="app_settings_reset_trusted_certificates_summary">Vergeet alle eerder geaccepteerde certificaten</string>
|
||||
<plurals name="app_settings_reset_trusted_certificates_success">
|
||||
<item quantity="one">Onbetrouwbaar certificaat</item>
|
||||
<item quantity="other">Onbetrouwbaar %d certificaten</item>
|
||||
</plurals>
|
||||
<string name="app_settings_debug">Debuggen</string>
|
||||
<string name="app_settings_log_to_external_storage">Log naar extern bestand</string>
|
||||
<string name="app_settings_log_to_external_storage_on">Loggen naar externe opslag (wanneer beschikbaar)</string>
|
||||
<string name="app_settings_log_to_external_storage_off">Extern bestands loggen uitgeschakeld</string>
|
||||
<string name="app_settings_show_debug_info">Debug info tonen</string>
|
||||
<string name="app_settings_show_debug_info_details">Bekijk/deel software configuratie details</string>
|
||||
<!--AccountActivity-->
|
||||
<string name="account_synchronize_now">Synchroniseer nu</string>
|
||||
<string name="account_synchronizing_now">Aan het synchronizeren...</string>
|
||||
<string name="account_settings">Account instellingen</string>
|
||||
<string name="account_delete">Account verwijderen</string>
|
||||
<string name="account_delete_confirmation_title">Account echt verwijderen?</string>
|
||||
<string name="account_delete_confirmation_text">Alle lokale kopieën van adresboeken, agenda\'s en taken worden verwijderd.</string>
|
||||
<string name="account_refresh_address_book_list">Adresboeken vernieuwen</string>
|
||||
<string name="account_create_new_address_book">Maak een nieuw adresboek</string>
|
||||
<string name="account_refresh_calendar_list">Agenda\'s vernieuwen</string>
|
||||
<string name="account_create_new_calendar">Maak een nieuwe agenda</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">DAVdroid rechten</string>
|
||||
<string name="permissions_calendar">Agenda rechten</string>
|
||||
<string name="permissions_calendar_details">Om CalDAV afspraken te synchroniseren met u agenda dient DAVdroid toegang te verkrijgen. </string>
|
||||
<string name="permissions_calendar_request">Agenda rechten verkrijgen</string>
|
||||
<string name="permissions_contacts">Contact rechten</string>
|
||||
<string name="permissions_contacts_details">Om CalDAV afspraken te synchroniseren met u contacten dient DAVdroid toegang te verkrijgen. </string>
|
||||
<string name="permissions_contacts_request">Contacten rechten verkrijgen</string>
|
||||
<string name="permissions_opentasks">OpenTasks rechten</string>
|
||||
<string name="permissions_opentasks_details">Om CalDAV taken te synchroniseren met uw local takenlijst dient DAVdroid toegang te hebben tot OpenTasks</string>
|
||||
<string name="permissions_opentasks_request">OpenTasks rechten verkrijgen</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">Account toevoegen</string>
|
||||
<string name="login_type_email">Inloggen met e-mailadres</string>
|
||||
<string name="login_email_address">Email adres</string>
|
||||
<string name="login_email_address_error">Geldig email adres vereist</string>
|
||||
<string name="login_password">Wachtwoord</string>
|
||||
<string name="login_password_required">Wachtwoord vereist</string>
|
||||
<string name="login_type_url">Inloggen met URL en gebruikersnaam</string>
|
||||
<string name="login_url_must_be_http_or_https">URL moet met http(s):// beginnen</string>
|
||||
<string name="login_url_host_name_required">Hostnaam vereist</string>
|
||||
<string name="login_user_name">Gebruikersnaam</string>
|
||||
<string name="login_user_name_required">Gebruikersnaam vereist</string>
|
||||
<string name="login_base_url">Basis URL</string>
|
||||
<string name="login_auth_preemptive">Preëmptieve authenticatie (aanbevolen, maar werkt niet met Digest-auth)</string>
|
||||
<string name="login_login">Login</string>
|
||||
<string name="login_back">Terug</string>
|
||||
<string name="login_create_account">Maak een account</string>
|
||||
<string name="login_account_name">Accountnaam</string>
|
||||
<string name="login_account_name_info">Gebruik je email adres als account naam want Android zal je account naam gebruiken als ORGANIZER veld voor gemaakte afspraken. Je kunt geen 2 accounts met dezelfde naam hebben,</string>
|
||||
<string name="login_account_name_required">Accountnaam vereist</string>
|
||||
<string name="login_account_not_created">Account kon niet gemaakt worden.</string>
|
||||
<string name="login_configuration_detection">Configuratie detectie</string>
|
||||
<string name="login_querying_server">Even geduld, verzoek naar server...</string>
|
||||
<string name="login_no_caldav_carddav">Kon geen CalDAV of CardDAV service vinden.</string>
|
||||
<string name="login_view_logs">Bekijk logs</string>
|
||||
<!--AccountSettingsActivity-->
|
||||
<string name="settings_title">Instellingen: %s</string>
|
||||
<string name="settings_authentication">Authenticatie</string>
|
||||
<string name="settings_username">Gebruikersnaam</string>
|
||||
<string name="settings_enter_username">Gebruikersnaam invoeren:</string>
|
||||
@@ -52,28 +151,62 @@
|
||||
<item>Elke 4 uur</item>
|
||||
<item>Dagelijks</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">Sync alleen tijdens WiFi</string>
|
||||
<string name="settings_sync_wifi_only_on">Synchronisatie is voorbehouden tijdens WiFi verbindingen</string>
|
||||
<string name="settings_sync_wifi_only_off">Verbinding type is niet overwogen</string>
|
||||
<string name="settings_sync_wifi_only_ssid">WiFi SSID beperking</string>
|
||||
<string name="settings_sync_wifi_only_ssid_on">Zal alleen synchroniseren over %s</string>
|
||||
<string name="settings_sync_wifi_only_ssid_off">Alle WiFI verbindingen mogen worden gebruikt</string>
|
||||
<string name="settings_sync_wifi_only_ssid_message">Type de naam van het WiFi netwerk (SSID) om synchronisatie tot dit netwerk te beperken. Leeg laten voor sync over alle netwerken.</string>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Tijdslimiet verleden afspraken</string>
|
||||
<string name="settings_sync_time_range_past_none">Alle afspraken worden gesynchronizeerd</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="one">Afspraken ouder dan een dag worden genegeerd</item>
|
||||
<item quantity="other">Afspraken ouder dan %d dagen worden genegeerd</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Afspraken ouder dan dit aantal dagen worden genegeerd (mag 0 zijn). Laat leeg om alle afspraken te synchronizeren.</string>
|
||||
<string name="settings_manage_calendar_colors">Agenda kleuren beheren</string>
|
||||
<string name="settings_manage_calendar_colors_on">Agenda kleuren worden door DAVdroid beheerd.</string>
|
||||
<string name="settings_manage_calendar_colors_off">Agenda kleuren worden niet door DAVdroid ingesteld</string>
|
||||
<string name="settings_version_update">DAVdroid versie update</string>
|
||||
<string name="settings_version_update_settings_updated">Interne instellingen zijn bijgewerkt.</string>
|
||||
<string name="settings_version_update_install_hint">Problemen? Deïnstalleer DAVdroid, daarna herinstalleren.</string>
|
||||
<!--collection management-->
|
||||
<string name="create_addressbook">Maak adresboek</string>
|
||||
<string name="create_addressbook_display_name_hint">Mijn adresboek</string>
|
||||
<string name="create_calendar">Maak CalDAV collectie</string>
|
||||
<string name="create_calendar_display_name_hint">Mijn agenda</string>
|
||||
<string name="create_calendar_time_zone">Tijdzone:</string>
|
||||
<string name="create_calendar_type">Collectie type:</string>
|
||||
<string name="create_calendar_type_only_events">Agenda (alleen afspraken)</string>
|
||||
<string name="create_calendar_type_only_tasks">Takenlijst (alleen taken)</string>
|
||||
<string name="create_calendar_type_events_and_tasks">Gecombineerd (afspraken en taken)</string>
|
||||
<string name="create_collection_color">Stel een collectie kleur in</string>
|
||||
<string name="create_collection_creating">Collectie aan het maken</string>
|
||||
<string name="create_collection_display_name">Weergave naam (titel) van deze collectie:</string>
|
||||
<string name="create_collection_display_name_required">Titel is vereist</string>
|
||||
<string name="create_collection_description">Beschrijving (optioneel):</string>
|
||||
<string name="create_collection_home_set">Begin map:</string>
|
||||
<string name="create_collection_create">Maak</string>
|
||||
<string name="delete_collection">Verwijder collectie</string>
|
||||
<string name="delete_collection_confirm_title">Weet je het zeker?</string>
|
||||
<string name="delete_collection_confirm_warning">Deze collectie (%s) en alle data zal verwijderd worden van de server.</string>
|
||||
<string name="delete_collection_deleting_collection">Collectie aan het verwijderen</string>
|
||||
<!--ExceptionInfoFragment-->
|
||||
<string name="exception">Er is een fout opgetreden.</string>
|
||||
<string name="exception_httpexception">Er is een HTTP fout opgetreden.</string>
|
||||
<string name="exception_ioexception">Er is een I/O fout opgetreden.</string>
|
||||
<string name="exception_show_details">Toon details</string>
|
||||
<!--sync errors and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Debug informatie</string>
|
||||
<string name="sync_error_permissions">DAVdroid rechten</string>
|
||||
<string name="sync_error_permissions_text">Aanvullende rechten vereist</string>
|
||||
<string name="sync_error_calendar">Agenda synchronisatie is mislukt (%s)</string>
|
||||
<string name="sync_error_contacts">Adresboek synchronisatie is mislukt (%s)</string>
|
||||
<string name="sync_error_tasks">Taak synchronisatie is mislukt (%s)</string>
|
||||
<string name="sync_error">Fout tijdens %s</string>
|
||||
<string name="sync_error_http_dav">Serverfout tijdens %s</string>
|
||||
<string name="sync_error_local_storage">Database fout tijdens %s</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>synchronisatie voorbereiden</item>
|
||||
<item>mogelijkheden opvragen</item>
|
||||
<item>lokaal verwijderde entries verwrken</item>
|
||||
<item>nieuwe/aangepaste entries verwerken</item>
|
||||
<item>nieuwe/aangepaste entries uploaden</item>
|
||||
<item>controleren van sync staat</item>
|
||||
<item>vermelden lokale entries</item>
|
||||
<item>vermelden server entries</item>
|
||||
<item>vergelijken van lokale/server entries</item>
|
||||
<item>downloaden server entries</item>
|
||||
<item>opslaan van sync staat</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">Gebruikersnaam/wachtwoord onjuist</string>
|
||||
</resources>
|
||||
|
||||
@@ -3,19 +3,98 @@
|
||||
<!--common strings-->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="help">Pomoc</string>
|
||||
<string name="manage_accounts">Zadządzaj kontami</string>
|
||||
<string name="please_wait">Proszę czekać</string>
|
||||
<string name="send">Wyślij</string>
|
||||
<!--startup dialogs-->
|
||||
<string name="startup_dont_show_again">Nie pokazuj ponownie</string>
|
||||
<string name="startup_development_version">DAVdroid Preview Release</string>
|
||||
<string name="startup_development_version_give_feedback">Przekaż opinię</string>
|
||||
<string name="startup_donate">Informacje Open-Source</string>
|
||||
<string name="startup_donate_now">Pokaż stronę darowizny</string>
|
||||
<string name="startup_donate_later">Może później</string>
|
||||
<string name="startup_google_play_accounts_removed">Informacje o błędzie DRM Sklepu Play</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Więcej informacji</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks nie jest zainstalowany</string>
|
||||
<string name="startup_opentasks_not_installed_message">Aplikacja Open Tasks nie jest dostępna, więc DAVdroid nie będzie mógł synchronizować listy zadań. </string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Po zainstalowaniu OpenTasks konieczne jest PRZEINSTALOWANIE DAVdroid i ponowne dodanie twoich kont (błąd Androida).</string>
|
||||
<string name="startup_opentasks_not_installed_install">Zainstaluj OpenTasks</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Warunki licencji</string>
|
||||
<!--global settings-->
|
||||
<string name="logging_davdroid_file_logging">Plik logów DAVdroid</string>
|
||||
<string name="logging_to_external_storage">Logowanie do zewnątrznej pamięci: %s</string>
|
||||
<string name="logging_to_external_storage_warning">Usuń logi tak szybko jak to możliwe!</string>
|
||||
<string name="logging_couldnt_create_file">Nie można stworzyć zewnętrznego pliku logów: %s</string>
|
||||
<string name="logging_no_external_storage">Zewnętrzna pamięci nie została naleziona</string>
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_open">Otwórz menu nawigacji</string>
|
||||
<string name="navigation_drawer_close">Zamknij menu nawigacji</string>
|
||||
<string name="navigation_drawer_subtitle">Adapter synchronizacji CalDAV/CardDAV</string>
|
||||
<string name="navigation_drawer_about">O DAVdroid / Licencja</string>
|
||||
<string name="navigation_drawer_settings">Ustawienia</string>
|
||||
<string name="navigation_drawer_news_updates">Nowości & aktualizacje
|
||||
</string>
|
||||
<string name="navigation_drawer_external_links">Zewnętrzne odnośniki</string>
|
||||
<string name="navigation_drawer_website">Strona WWW</string>
|
||||
<string name="navigation_drawer_faq">FQA</string>
|
||||
<string name="navigation_drawer_forums">Społeczność</string>
|
||||
<string name="navigation_drawer_donate">Dotacja</string>
|
||||
<!--DavService-->
|
||||
<string name="dav_service_refresh_failed">Wykrycie serwisu nie powiodło się</string>
|
||||
<string name="dav_service_refresh_couldnt_refresh">Nie można odświeżyć listy kolekcji</string>
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">Ustawienia</string>
|
||||
<string name="app_settings_user_interface">
|
||||
Interfejs użytkownika</string>
|
||||
<string name="app_settings_reset_hints">Zresetuj podpowiedzi</string>
|
||||
<string name="app_settings_security">Bezpieczeństwo</string>
|
||||
<string name="app_settings_debug">Debugowanie</string>
|
||||
<string name="app_settings_log_to_external_storage">Loguj to zewnętrznego pliku</string>
|
||||
<string name="app_settings_show_debug_info">Pokaż informacje do debug\'owania</string>
|
||||
<!--AccountActivity-->
|
||||
<string name="account_synchronize_now">Synchronizuj teraz</string>
|
||||
<string name="account_synchronizing_now">Synchronizcja w toku</string>
|
||||
<string name="account_settings">Ustawienia konta</string>
|
||||
<string name="account_delete">Usuń konto</string>
|
||||
<string name="account_delete_confirmation_title">Naprawdę chcesz usunąć konto?</string>
|
||||
<string name="account_refresh_address_book_list">Odśwież list książek adresowych</string>
|
||||
<string name="account_create_new_address_book">Stwórz nową książkę adresową</string>
|
||||
<string name="account_refresh_calendar_list">Odśwież liste kalendarzy</string>
|
||||
<string name="account_create_new_calendar">Stwórz nowy kalendarz</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">Uprawnienia DAVdroid</string>
|
||||
<string name="permissions_calendar">Uprawnienia kalendarza</string>
|
||||
<string name="permissions_calendar_request">Zezwól na uprawnienia kalendarza</string>
|
||||
<string name="permissions_contacts">Uprawnienia kontaktów</string>
|
||||
<string name="permissions_contacts_request">Zezwól na uprawnienia kontaktów</string>
|
||||
<string name="permissions_opentasks">Uprawnienia OpenTasks</string>
|
||||
<string name="permissions_opentasks_request">Zezwól na uprawnienia OpenTasks</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">Dodaj konto</string>
|
||||
<string name="login_type_email">Logowanie za pomocą adresu e-mail</string>
|
||||
<string name="login_email_address">Adres e-mail</string>
|
||||
<string name="login_email_address_error">Wymanagny poprawny adres e-mail</string>
|
||||
<string name="login_password">Hasło</string>
|
||||
<string name="login_password_required">Wymagane hasło</string>
|
||||
<string name="login_type_url">Logowanie za pomocą adresu URL i nazwy użytkownika</string>
|
||||
<string name="login_url_host_name_required">Wymagana nazwa hosta</string>
|
||||
<string name="login_user_name">Nazwa użytkownika</string>
|
||||
<string name="login_user_name_required">Wymagana nazwa użtkonika</string>
|
||||
<string name="login_base_url">Podstawowy URL</string>
|
||||
<string name="login_auth_preemptive">Autoryzacja prewencyjna (zalecana, ale niezgodna z autoryzacją Digest)</string>
|
||||
<string name="login_login">Zaloguj</string>
|
||||
<string name="login_back">Wróć</string>
|
||||
<string name="login_create_account">Stwórz konto</string>
|
||||
<string name="login_account_name">Nazwa konta</string>
|
||||
<string name="login_account_name_info">Użyj swojego adresu e-mail jako nazwy konta, ponieważ Android będzie używał nazwy konta jako pola ORGANIZATOR dla wydarzeń, które stworzysz. Nie możesz posiadać dwóch kont o takiej samej nazwie.</string>
|
||||
<string name="login_account_name_required">Wymagana nazwa konta</string>
|
||||
<string name="login_account_not_created">Konto nie mogło zostać stworzone</string>
|
||||
<string name="login_configuration_detection">Wykrywanie konfiguracji</string>
|
||||
<string name="login_querying_server">Proszę czekać, odpytywanie serwera...</string>
|
||||
<string name="login_view_logs">Pokaż logi</string>
|
||||
<!--AccountSettingsActivity-->
|
||||
<string name="settings_title">Ustawienia: %s</string>
|
||||
<string name="settings_authentication">Uwierzytelnianie</string>
|
||||
<string name="settings_username">Nazwa użytkownika</string>
|
||||
<string name="settings_enter_username">Wpisz nazwe użytkownika:</string>
|
||||
@@ -52,28 +131,54 @@
|
||||
<item>Co 4 godziny</item>
|
||||
<item>Raz dziennie</item>
|
||||
</string-array>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">Metoda grupowania kontaktów</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string name="settings_rfc6868_for_vcards">Użyj RFC6868 dla VCards</string>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_manage_calendar_colors">Zarządzaj kolorami kalendarza</string>
|
||||
<string name="settings_manage_calendar_colors_on">Kolory kalendarza są zarządzane przez DAVdroid</string>
|
||||
<string name="settings_manage_calendar_colors_off">Kolory kalendarze nie są ustawiane przez DAVdroid</string>
|
||||
<string name="settings_version_update">Aktualizacji wersji DAVdroid</string>
|
||||
<string name="settings_version_update_install_hint">Problemy? Odinstaluj DAVdroid i zainstaluj ponownie.</string>
|
||||
<!--collection management-->
|
||||
<string name="create_addressbook">Stwórz książkę adresową</string>
|
||||
<string name="create_addressbook_display_name_hint">Moja książka adresowa</string>
|
||||
<string name="create_calendar">Stwórz kolekcje CalDAV</string>
|
||||
<string name="create_calendar_display_name_hint">Mój kalendarz</string>
|
||||
<string name="create_calendar_time_zone">Strefa czasowa:</string>
|
||||
<string name="create_calendar_type">Typ kolekcji:</string>
|
||||
<string name="create_calendar_type_only_events">Kalendarz (tylko wydarzenia)</string>
|
||||
<string name="create_calendar_type_only_tasks">Lista zadań (tylko zadań)</string>
|
||||
<string name="create_calendar_type_events_and_tasks">Połączone (wydarzenia i zadania)</string>
|
||||
<string name="create_collection_color">Ustaw kolor kolekcji</string>
|
||||
<string name="create_collection_creating">Tworzenie kolekcji</string>
|
||||
<string name="create_collection_display_name">Nazwa wyświetlana (tytuł) kolekcji:</string>
|
||||
<string name="create_collection_display_name_required">Tytuł jest wymagany</string>
|
||||
<string name="create_collection_description">Opis (opcjoalnie)</string>
|
||||
<string name="create_collection_home_set">Ustaw początek:</string>
|
||||
<string name="create_collection_create">Stwórz</string>
|
||||
<string name="delete_collection">Usuń kolekcje</string>
|
||||
<string name="delete_collection_confirm_title">Jesteś pewien?</string>
|
||||
<string name="delete_collection_confirm_warning">Kolekcja (%s) i jej wszystkie dane zostaną usunięte z serwera.</string>
|
||||
<string name="delete_collection_deleting_collection">Usuwanie kolekcji</string>
|
||||
<!--ExceptionInfoFragment-->
|
||||
<string name="exception">Wystąpił błąd.</string>
|
||||
<string name="exception_httpexception">Wystąpił błąd HTTP.</string>
|
||||
<string name="exception_ioexception">Wystąpił błąd I/O.</string>
|
||||
<string name="exception_show_details">Pokaż szczegóły</string>
|
||||
<!--sync errors and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Informacje debugowe</string>
|
||||
<string name="sync_error_permissions">Uprawnienia DAVdroid</string>
|
||||
<string name="sync_error_permissions_text">Wymagane dodatkowe uprawnienia</string>
|
||||
<string name="sync_error_calendar">Synchronizacja kalendarza nie powiodała się (%s)</string>
|
||||
<string name="sync_error_contacts">Synchronizacja książki adresowej nie powiodała się (%s)</string>
|
||||
<string name="sync_error_tasks">Synchronizacja zadań nie powiodała się (%s)</string>
|
||||
<string name="sync_error">Błąd podczas %s</string>
|
||||
<string name="sync_error_http_dav">Błąd servera podczas %s</string>
|
||||
<string name="sync_error_local_storage">Bład bazy danych podczas %s</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>przygotowania synchronizacji</item>
|
||||
<item>odpytywania o funkcje</item>
|
||||
<item>przetwarzania lokalnie usuniętych wpisów</item>
|
||||
<item>przygotowania stworzonych lub zmodyfikowanych wpisów</item>
|
||||
<item>wysyłania stworzonych lub zmodyfikowanych wpisów</item>
|
||||
<item>sprawdzania stanu synchronizacji</item>
|
||||
<item>listowania lokalnych wpisów</item>
|
||||
<item>listowania zdalnych wpisów</item>
|
||||
<item>porównywania lokalnych lub zdalnych wpisów</item>
|
||||
<item>pobierania zdalnych wpisów</item>
|
||||
<item>zapisywania stanu synchronizacji</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">Błędna nazwa użytkownika lub hasło</string>
|
||||
</resources>
|
||||
|
||||
@@ -12,14 +12,15 @@
|
||||
<string name="startup_development_version_message">Esta é uma versão de desenvolvimento do DAVdroid. Lembre-se de que as coisas podem não funcionar como esperado. Por favor, envie sugestões e comentários construtivos para melhorar o DAVdroid.</string>
|
||||
<string name="startup_development_version_give_feedback">Enviar sugestões</string>
|
||||
<string name="startup_donate">Informação sobre Código Aberto</string>
|
||||
<string name="startup_donate_message">Estamos felizes que você usa o DAVdroid, que é um software de código aberto (GPLv3). O desenvolvimento do DAVdroid é trabalhoso e consome muitas horas de trabalho. Por esse motivo, considere fazer uma doação.</string>
|
||||
<string name="startup_donate_message">Estamos felizes que você usa o DAVdroid, um software de código aberto (GPLv3). O desenvolvimento do DAVdroid é trabalhoso e consome muitas horas de trabalho. Por esse motivo, considere fazer uma doação.</string>
|
||||
<string name="startup_donate_now">Mostrar a página de doações</string>
|
||||
<string name="startup_donate_later">Talvez depois</string>
|
||||
<string name="startup_google_play_accounts_removed">Informação sobre o erro de DRM da Play Store</string>
|
||||
<string name="startup_google_play_accounts_removed_message">Sob certas condições, o DRM da Play Store pode fazer com que todas as contas DAVdroid sejam perdidas depois de uma reinicialização ou atualização do DAVdroid. Se você for afetado por esse problema, instale o \"DAVdroid JB Workaround\" a partir da Play Store.</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Mais informações</string>
|
||||
<string name="startup_opentasks_not_installed">O OpenTasks não está instalado</string>
|
||||
<string name="startup_opentasks_not_installed_message">O aplicativo OpenTasks não está instalado ou está inacessível, não sendo possível sincronizar suas listas de tarefas com o DAVdroid. Reinstale o DAVdroid e então adicione suas contas novamente depois da instalação do OpenTasks.</string>
|
||||
<string name="startup_opentasks_not_installed_message">O aplicativo OpenTasks não está disponível, não sendo possível sincronizar as listas de tarefas pelo DAVdroid.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Depois da instalação do OpenTasks, torna-se necessário REINSTALAR o DAVdroid e adicionar suas contas novamente (erro do Android).</string>
|
||||
<string name="startup_opentasks_not_installed_install">Instalar o OpenTasks</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Termos da Licença</string>
|
||||
@@ -76,6 +77,17 @@
|
||||
<string name="account_create_new_address_book">Criar novo livro de endereços</string>
|
||||
<string name="account_refresh_calendar_list">Atualizar lista de calendários</string>
|
||||
<string name="account_create_new_calendar">Criar novo calendário</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">Permissões do DAVdroid</string>
|
||||
<string name="permissions_calendar">Permissões do calendário</string>
|
||||
<string name="permissions_calendar_details">Para sincronizar os eventos CalDAV com seus calendários locais, o DAVdroid precisa acessar seus calendários.</string>
|
||||
<string name="permissions_calendar_request">Solicitar permissão do calendário</string>
|
||||
<string name="permissions_contacts">Permissões dos contados</string>
|
||||
<string name="permissions_contacts_details">Para sincronizar livros de endereços CardDAV com seus contatos locais, o DAVdroid precisa acessar seus contatos.</string>
|
||||
<string name="permissions_contacts_request">Solicitar permissão dos contatos</string>
|
||||
<string name="permissions_opentasks">Permissões do OpenTasks</string>
|
||||
<string name="permissions_opentasks_details">Para sincronizar tarefas CalDAV com suas listas de tarefas locais, o DAVdroid precisa acessar o OpenTasks.</string>
|
||||
<string name="permissions_opentasks_request">Solicitar permissão do OpenTasks</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">Adicionar conta</string>
|
||||
<string name="login_type_email">Autenticação com endereço de e-mail</string>
|
||||
@@ -95,6 +107,7 @@
|
||||
<string name="login_create_account">Criar conta</string>
|
||||
<string name="login_account_name">Nome da conta</string>
|
||||
<string name="login_account_name_info">Use seu endereço de e-mail como nome da conta porque o Android irá usar esse nome como campo AGENDA nos eventos que você criar. Não é possível ter duas contas com o mesmo nome.</string>
|
||||
<string name="login_account_contact_group_method">Método do grupo Contato:</string>
|
||||
<string name="login_account_name_required">É necessário um nome de conta</string>
|
||||
<string name="login_account_not_created">A conta não pôde ser criada</string>
|
||||
<string name="login_configuration_detection">Detecção de configuração</string>
|
||||
@@ -139,13 +152,37 @@
|
||||
<item>A cada 4 horas</item>
|
||||
<item>Uma vez por dia</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_time_range_past">Limite para eventos passados</string>
|
||||
<string name="settings_sync_wifi_only">Sincronizar apenas por Wi-Fi</string>
|
||||
<string name="settings_sync_wifi_only_on">Sincronização restrita a conexões Wi-Fi</string>
|
||||
<string name="settings_sync_wifi_only_off">O tipo de conexão não é considerado</string>
|
||||
<string name="settings_sync_wifi_only_ssid">Restrição de SSID Wi-Fi</string>
|
||||
<string name="settings_sync_wifi_only_ssid_on">Sincronizará apenas com %s</string>
|
||||
<string name="settings_sync_wifi_only_ssid_off">Qualquer conexão Wi-Fi pode ser utilizada</string>
|
||||
<string name="settings_sync_wifi_only_ssid_message">Informe o nome da rede Wi-Fi (SSID) que será usada para sincronização ou deixe em branco para usar qualquer conexão Wi-Fi.</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">Método do grupo Contato</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string-array name="settings_contact_group_method_entries">
|
||||
<item>Grupos são VCards separados</item>
|
||||
<item>Grupos são categorias por contato</item>
|
||||
</string-array>
|
||||
<string name="settings_rfc6868_for_vcards">Usar RFC6868 para VCards</string>
|
||||
<string name="settings_rfc6868_for_vcards_on">Aspas podem ser utilizadas nos valores dos parâmetros</string>
|
||||
<string name="settings_rfc6868_for_vcards_off">Aspas não podem ser utilizadas nos valores dos parâmetros</string>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Limite de tempo para eventos passados</string>
|
||||
<string name="settings_sync_time_range_past_none">Todos os eventos serão sincronizados</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="one">Os eventos que ocorreram a mais de um dia serão ignorados</item>
|
||||
<item quantity="other">Eventos que ocorreram a mais de %d dias serão ignorados</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Os eventos que ocorreram antes desse número de dias serão ignorados (pode ser 0). Deixe em branco para sincronizar todos os eventos.</string>
|
||||
<string name="settings_manage_calendar_colors">Gerenciar cores dos calendários</string>
|
||||
<string name="settings_manage_calendar_colors_on">Cores dos calendários definidas pelo DAVdroid</string>
|
||||
<string name="settings_manage_calendar_colors_off">Cores dos calendários não definidas pelo DAVdroid</string>
|
||||
<string name="settings_version_update">Atualização da versão do DAVdroid</string>
|
||||
<string name="settings_version_update_settings_updated">As configurações internas foram atualizadas.</string>
|
||||
<string name="settings_version_update_install_hint">Problemas? Desinstale o DAVdroid e instale novamente.</string>
|
||||
@@ -177,6 +214,8 @@
|
||||
<string name="exception_show_details">Mostrar detalhes</string>
|
||||
<!--sync errors and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Informações de depuração</string>
|
||||
<string name="sync_error_permissions">Permissões do DAVdroid</string>
|
||||
<string name="sync_error_permissions_text">É necessário permissões adicionais</string>
|
||||
<string name="sync_error_calendar">Falha na sincronização do calendário (%s)</string>
|
||||
<string name="sync_error_contacts">Falha na sincronização do livro de endereços (%s)</string>
|
||||
<string name="sync_error_tasks">Falha na sincronização das tarefas (%s)</string>
|
||||
@@ -185,7 +224,7 @@
|
||||
<string name="sync_error_local_storage">Erro do banco de dados ao %s</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>preparando sincronização</item>
|
||||
<item>procurando capacidades</item>
|
||||
<item>procurando habilidades</item>
|
||||
<item>processando os itens excluídos localmente</item>
|
||||
<item>preparando os itens criados/modificados</item>
|
||||
<item>enviando os itens criados/modificados</item>
|
||||
@@ -194,6 +233,7 @@
|
||||
<item>listando os itens remotos</item>
|
||||
<item>comparando os itens locais/remotos</item>
|
||||
<item>baixando os itens remotos</item>
|
||||
<item>pós-processamento</item>
|
||||
<item>salvando o estado da sincronização</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">Usuário/senha incorreto</string>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<!--DavService-->
|
||||
<!--AppSettingsActivity-->
|
||||
<!--AccountActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_type_email">Login com seu enderço de email</string>
|
||||
<string name="login_type_url">Login com URL e nome do usuário</string>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<!--DavService-->
|
||||
<!--AppSettingsActivity-->
|
||||
<!--AccountActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_type_email">Вход по адресу email</string>
|
||||
<string name="login_type_url">Вход через URL и имя пользователя</string>
|
||||
|
||||
@@ -19,9 +19,12 @@
|
||||
<string name="startup_google_play_accounts_removed_message">Под одређеним околностима ДРМ Плеј продавнице може да узрокује да сви налози ДАВдроида нестане након рестарта или ажурирања ДАВдроида. Ако и само ако имате овај проблем, инсталирајте „DAVdroid JB Workaround“ са Плеј продавнице.</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Још информација</string>
|
||||
<string name="startup_opentasks_not_installed">Отворени задаци нису инсталирани</string>
|
||||
<string name="startup_opentasks_not_installed_message">Отворени задаци нису инсталирани или доступни, па ДАВдроид неће моћи да синхронизује листе задатака. Поново инсталирајте ДАВдроид и поново додајте ваше налоге након инсталације Отворених задатака.</string>
|
||||
<string name="startup_opentasks_not_installed_message">Отворени задаци нису доступни па ДАВдроид неће моћи да синхронизује листе задатака. </string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">Након инсталирања Отворених задатака, морате поново да инсталирате ДАВдроид и поново додате ваше налоге (због грешке у Андроиду).</string>
|
||||
<string name="startup_opentasks_not_installed_install">Инсталирај Отворене задатке</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Услови лиценце</string>
|
||||
<string name="about_license_info_no_warranty">Овај програм НЕМА НИКАКВЕ ГАРАНЦИЈЕ. Бесплатан је софтвер којег можете слободно да делите под одређеним условима.</string>
|
||||
<!--global settings-->
|
||||
<string name="logging_davdroid_file_logging">ДАВдроид евиденција</string>
|
||||
<string name="logging_to_external_storage">Уписивање евиденције у спољашње складиште: %s</string>
|
||||
@@ -48,14 +51,22 @@
|
||||
<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_security">Безбедност</string>
|
||||
<string name="app_settings_reset_trusted_certificates">Ресетуј поуздане сертификате</string>
|
||||
<string name="app_settings_reset_trusted_certificates_summary">Заборављање претходно прихваћених сертификата</string>
|
||||
<plurals name="app_settings_reset_trusted_certificates_success">
|
||||
<item quantity="one">Посумњано у један сертификат</item>
|
||||
<item quantity="few">Посумњано у %d сертификата</item>
|
||||
<item quantity="other">Посумњано у %d сертификата</item>
|
||||
</plurals>
|
||||
<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>
|
||||
@@ -67,6 +78,17 @@
|
||||
<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">ДАВдроид дозволе</string>
|
||||
<string name="permissions_calendar">Дозволе за календар</string>
|
||||
<string name="permissions_calendar_details">Да би синхронизовао КалДАВ догађаје са вашим локалним календарима, ДАВдроиду треба приступ вашим календарима.</string>
|
||||
<string name="permissions_calendar_request">Захтевај дозволе за календар</string>
|
||||
<string name="permissions_contacts">Дозволе за контакте</string>
|
||||
<string name="permissions_contacts_details">Да би синхронизовао КардДАВ адресаре са вашим локалним контактима, ДАВдроиду треба приступ вашим контактима.</string>
|
||||
<string name="permissions_contacts_request">Захтевај дозволе за контакте</string>
|
||||
<string name="permissions_opentasks">Дозволе за задатке</string>
|
||||
<string name="permissions_opentasks_details">Да би синхронизовао КалДАВ задатке са вашим локалним листама задатака, ДАВдроиду треба приступ Задацима.</string>
|
||||
<string name="permissions_opentasks_request">Захтевај дозволе за задатке</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">Додај налог</string>
|
||||
<string name="login_type_email">Пријавите се адресом е-поште</string>
|
||||
@@ -86,6 +108,7 @@
|
||||
<string name="login_create_account">Направи налог</string>
|
||||
<string name="login_account_name">Назив налога</string>
|
||||
<string name="login_account_name_info">Користите вашу е-адресу за назив налога јер Андроид користи назив налога за поље ОРГАНИЗАТОР за догађаје које направите. Не можете имати два налога истог назива.</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>
|
||||
@@ -130,6 +153,38 @@
|
||||
<item>Свака 4 сата</item>
|
||||
<item>Једном дневно</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">Само преко бежичног</string>
|
||||
<string name="settings_sync_wifi_only_on">Синхронизовање само преко бежичних мрежа</string>
|
||||
<string name="settings_sync_wifi_only_off">Тип везе није узет у обзир</string>
|
||||
<string name="settings_sync_wifi_only_ssid">Ограничења ССИД-а бежичних</string>
|
||||
<string name="settings_sync_wifi_only_ssid_on">Синхронизовање само преко %s</string>
|
||||
<string name="settings_sync_wifi_only_ssid_off">Коришћење свих бежичних мрежа</string>
|
||||
<string name="settings_sync_wifi_only_ssid_message">Унесите назив бежичне мреже (њен ССИД) да бисте ограничили синхронизацију само на ту мрежу, или оставите празно за синхронизовање преко било које бежичне мреже.</string>
|
||||
<string name="settings_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>Групе су одвојене В-карте</item>
|
||||
<item>Групе су категорије по контакту</item>
|
||||
</string-array>
|
||||
<string name="settings_rfc6868_for_vcards">Користи РФЦ6868 за В-карте</string>
|
||||
<string name="settings_rfc6868_for_vcards_on">Двоструки наводници могу да се користе у вредностима параметара</string>
|
||||
<string name="settings_rfc6868_for_vcards_off">Двоструки наводници не могу да се користе у вредностима параметара</string>
|
||||
<string name="settings_caldav">КалДАВ</string>
|
||||
<string name="settings_sync_time_range_past">Ограничење догађаја у прошлости</string>
|
||||
<string name="settings_sync_time_range_past_none">Сви догађаји се синхронизују</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="one">Догађаји старији од једног дана ће бити занемарени</item>
|
||||
<item quantity="few">Догађаји старији од %d дана ће бити занемарени</item>
|
||||
<item quantity="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">Бојама календара управља ДАВдроид</string>
|
||||
<string name="settings_manage_calendar_colors_off">Боје календара није поставио ДАВдроид</string>
|
||||
<string name="settings_version_update">Надоградња ДАВдроид издања</string>
|
||||
<string name="settings_version_update_settings_updated">Унутрашње поставке су ажуриране.</string>
|
||||
<string name="settings_version_update_install_hint">Проблеми? Уклоните ДАВдроид па га поново инсталирајте.</string>
|
||||
@@ -148,6 +203,7 @@
|
||||
<string name="create_collection_display_name">Назив (наслов) ове збирке:</string>
|
||||
<string name="create_collection_display_name_required">Наслов је обавезан</string>
|
||||
<string name="create_collection_description">Опис (необавезан):</string>
|
||||
<string name="create_collection_home_set">Домаћа фасцикла:</string>
|
||||
<string name="create_collection_create">Направи</string>
|
||||
<string name="delete_collection">Обриши збирку</string>
|
||||
<string name="delete_collection_confirm_title">Да ли сте сигурни?</string>
|
||||
@@ -160,6 +216,8 @@
|
||||
<string name="exception_show_details">Прикажи детаље</string>
|
||||
<!--sync errors and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Подаци за исправљање грешака</string>
|
||||
<string name="sync_error_permissions">ДАВдроид дозволе</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>
|
||||
@@ -167,17 +225,18 @@
|
||||
<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>
|
||||
<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>
|
||||
</resources>
|
||||
|
||||
@@ -3,19 +3,118 @@
|
||||
<!--common strings-->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="help">Yardım</string>
|
||||
<string name="manage_accounts">Hesapları yönet</string>
|
||||
<string name="please_wait">Lütfen bekle ...</string>
|
||||
<string name="send">Gönder</string>
|
||||
<!--startup dialogs-->
|
||||
<string name="startup_dont_show_again">Bir daha gösterme</string>
|
||||
<string name="startup_development_version">DAVDroid Önizlenim Dağıtımı</string>
|
||||
<string name="startup_development_version_message">Bu DAVdroid\'in bir geliştirme sürümüdür. Bazı şeyler beklendiği gibi çalışmayabilir. DAVdroid\'i iyileştirmek için bize lütfen yapıcı eleştirilerini ilet.</string>
|
||||
<string name="startup_development_version_give_feedback">Geribildirim ver</string>
|
||||
<string name="startup_donate">Açık-Kaynak Bilgisi</string>
|
||||
<string name="startup_donate_message">Açık kaynaklı yazılım (GPLv3) olan DAVdroid\'i kullandığına çok mutluyuz. DAVdroid\'i geliştirmek zor bir iş ve üzerinde binlerce saat çalıştığımızdan, lütfen bir bağışta bulunmayı düşün.</string>
|
||||
<string name="startup_donate_now">Bağış sayfasını göster</string>
|
||||
<string name="startup_donate_later">Belki sonra</string>
|
||||
<string name="startup_google_play_accounts_removed">Play Store DRM hata bilgisi</string>
|
||||
<string name="startup_google_play_accounts_removed_message">Bazı durumlarda Play Store DRM\'i, cihazı yeniden başlatınca veya DAVdroid\'i yükseltince DAVdroid hesaplarının yokolmasına sebep olabiliyor. Bu sorundan etkileniyorsan (ve sadece bu durumda), lütfen Play Store\'daki \"DAVdroid JB Workaround\" uygulamasını kur.</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">Daha fazla bilgi</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks kurulu değil</string>
|
||||
<string name="startup_opentasks_not_installed_message">OpenTasks uygulaması yok, dolayısıyla DAVdroid iş listelerini senkronize edemeyecektir.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">OpenTasks\'i kurduktan sonra, DAVdroid\'i YENİDEN KURMAN ve hesaplarını yeniden eklemen gerek. (Android hatası).</string>
|
||||
<string name="startup_opentasks_not_installed_install">OpenTasks kur</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">Lisans şartları</string>
|
||||
<string name="about_license_info_no_warranty">Bu uygulama HİÇ BİR GARANTİ ile gelmemektedir. Bedava bir yazılımdır ve belli koşullar altında dağıtabilirsiniz.</string>
|
||||
<!--global settings-->
|
||||
<string name="logging_davdroid_file_logging">DAVdroid dosya jurnallemesi</string>
|
||||
<string name="logging_to_external_storage">Harici depolamaya jurnalleniyor: %s</string>
|
||||
<string name="logging_to_external_storage_warning">Jurnalleri bir an önce silin!</string>
|
||||
<string name="logging_couldnt_create_file">Harici jurnal dosyası yaratılamadı: %s </string>
|
||||
<string name="logging_no_external_storage">Harici depolama alanı bulunamadı</string>
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_open">Navigasyon çekmecesini aç</string>
|
||||
<string name="navigation_drawer_close">Navigasyon çekmecesini kapat</string>
|
||||
<string name="navigation_drawer_subtitle">CalDAV/CardDAV Senkronizasyon Adaptörü</string>
|
||||
<string name="navigation_drawer_about">Hakkında / Lisans</string>
|
||||
<string name="navigation_drawer_settings">Ayarlar</string>
|
||||
<string name="navigation_drawer_news_updates">Haberler & güncellemeler </string>
|
||||
<string name="navigation_drawer_external_links">Harici bağlantılar</string>
|
||||
<string name="navigation_drawer_website">Web sitesi</string>
|
||||
<string name="navigation_drawer_faq">SSS</string>
|
||||
<string name="navigation_drawer_forums">Camia</string>
|
||||
<string name="navigation_drawer_donate">Bağış yap</string>
|
||||
<string name="account_list_empty">DAVdroid\'e hoşgeldin!\n\nŞimdi bir CalDAV/CardDAV hesabı ekleyebilirsin.</string>
|
||||
<!--DavService-->
|
||||
<string name="dav_service_refresh_failed">Servis keşfi başarısız</string>
|
||||
<string name="dav_service_refresh_couldnt_refresh">Kolleksiyon listesi yenilenemedi</string>
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">Ayarlar</string>
|
||||
<string name="app_settings_user_interface">Kullanıcı arayüzü</string>
|
||||
<string name="app_settings_reset_hints">İpuçlarını sıfırla</string>
|
||||
<string name="app_settings_reset_hints_summary">Daha önceden azat edilen ipuçlarını yeniden etkinleştirir</string>
|
||||
<string name="app_settings_reset_hints_success">Tüm ipuçları artık gösterilecek</string>
|
||||
<string name="app_settings_security">Güvenlik</string>
|
||||
<string name="app_settings_reset_trusted_certificates">Güvenilen sertifikaları sıfırla</string>
|
||||
<string name="app_settings_reset_trusted_certificates_summary">Daha önceden kabul edilmiş tüm sertifikaları unutur</string>
|
||||
<plurals name="app_settings_reset_trusted_certificates_success">
|
||||
<item quantity="other">Güvenilmeyen %d sertifikaları</item>
|
||||
</plurals>
|
||||
<string name="app_settings_debug">Hata ayıklama</string>
|
||||
<string name="app_settings_log_to_external_storage">Harici dosyaya jurnalle</string>
|
||||
<string name="app_settings_log_to_external_storage_on">Harici depolamaya jurnalleniyor (eğer uygunsa)</string>
|
||||
<string name="app_settings_log_to_external_storage_off">Harici depolama mevcut değil</string>
|
||||
<string name="app_settings_show_debug_info">Hata ayıklama bilgilerini göster</string>
|
||||
<string name="app_settings_show_debug_info_details">Yazılım ve konfigürasyon detaylarına bak/paylaş</string>
|
||||
<!--AccountActivity-->
|
||||
<string name="account_synchronize_now">Şimdi senkronize et</string>
|
||||
<string name="account_synchronizing_now">Senkronize ediyor</string>
|
||||
<string name="account_settings">Hesap ayarları</string>
|
||||
<string name="account_delete">Hesabı sil</string>
|
||||
<string name="account_delete_confirmation_title">Hesap gerçekten silinsin mi?</string>
|
||||
<string name="account_delete_confirmation_text">Rehber, takvim ve iş listelerinin tüm yerel kopyaları silinecektir.</string>
|
||||
<string name="account_refresh_address_book_list">Rehber listesini yenile</string>
|
||||
<string name="account_create_new_address_book">Yeni rehber oluştur</string>
|
||||
<string name="account_refresh_calendar_list">Takvim listesini yenile</string>
|
||||
<string name="account_create_new_calendar">Yeni takvim oluştur</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">DAVdroid izinleri</string>
|
||||
<string name="permissions_calendar">Takvim izinleri</string>
|
||||
<string name="permissions_calendar_details">Takvim (sadece olaylar)
|
||||
</string>
|
||||
<string name="permissions_calendar_request">Takvim izinleri iste</string>
|
||||
<string name="permissions_contacts">Kişiler izinleri</string>
|
||||
<string name="permissions_contacts_details">CardDAV rehberlerinin cihazınızdaki kişilerinizle senkronize edebilmek için, DAVdroid cihazınızdaki kişilerinize erişime ihtiyacı vardır.</string>
|
||||
<string name="permissions_contacts_request">Kişiler izinleri iste</string>
|
||||
<string name="permissions_opentasks">OpenTasks izinleri</string>
|
||||
<string name="permissions_opentasks_details">CalDav iş listelerinizi yerel iş listelerinizle senkronize edebilmek için DAVdroid\'in OpenTasks\'e erişime ihtiyacı vardır.</string>
|
||||
<string name="permissions_opentasks_request">OpenTasks izinleri iste</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">Hesap ekle</string>
|
||||
<string name="login_type_email">Eposta adresi ile giriş yap</string>
|
||||
<string name="login_email_address">Eposta adresi</string>
|
||||
<string name="login_email_address_error">Geçerli eposta adresi zorunludur</string>
|
||||
<string name="login_password">Parola</string>
|
||||
<string name="login_password_required">Parola zorunludur</string>
|
||||
<string name="login_type_url">URL ve kullanıcı adı ile giriş yap</string>
|
||||
<string name="login_url_must_be_http_or_https">URL http(s):// ile başlamalıdır</string>
|
||||
<string name="login_url_host_name_required">Sunucu adı zorunludur</string>
|
||||
<string name="login_user_name">Kullanıcı adı</string>
|
||||
<string name="login_user_name_required">Kullanıcı adı zorunludur</string>
|
||||
<string name="login_base_url">Baz URL</string>
|
||||
<string name="login_auth_preemptive">Önleyici doğrulama (tavsiye edilir, fakat Digest doğrulama ile uyumsuz)</string>
|
||||
<string name="login_login">Giriş</string>
|
||||
<string name="login_back">Geri</string>
|
||||
<string name="login_create_account">Hesap yarat</string>
|
||||
<string name="login_account_name">Hesap adı</string>
|
||||
<string name="login_account_name_info">Hesap ismi olarak e-posta adresini kullan çünkü Android hesap ismini yarattığın olaylarda DÜZENLEYEN alanında kullanacaktır. Aynı isimde iki faklı hesabın olamaz.</string>
|
||||
<string name="login_account_name_required">Hesap adı zorunludur</string>
|
||||
<string name="login_account_not_created">Hesap yaratılamadı</string>
|
||||
<string name="login_configuration_detection">Konfigürasyon keşfi</string>
|
||||
<string name="login_querying_server">Lütfen bekle, sunucu sorgulanıyor...</string>
|
||||
<string name="login_no_caldav_carddav">CalDAV veya CardDAV servisi bulunamadı.</string>
|
||||
<string name="login_view_logs">Jurnallere bak</string>
|
||||
<!--AccountSettingsActivity-->
|
||||
<string name="settings_title">Ayarlar: %s</string>
|
||||
<string name="settings_authentication">Doğrulama</string>
|
||||
<string name="settings_username">Kullanıcı adı</string>
|
||||
<string name="settings_enter_username">Kullanıcı adı girin:</string>
|
||||
@@ -52,28 +151,62 @@
|
||||
<item>Her 4 saatte bir</item>
|
||||
<item>Günde bir</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">Sadece WiFi üzerinden senkronize et</string>
|
||||
<string name="settings_sync_wifi_only_on">Senkronizasyon WiFi bağlantıları ile kısıtlıdır</string>
|
||||
<string name="settings_sync_wifi_only_off">Bağlantı tipi göz önünde bulundurulmaz</string>
|
||||
<string name="settings_sync_wifi_only_ssid">WiFi SSID kısıtlaması</string>
|
||||
<string name="settings_sync_wifi_only_ssid_on">Sadece %s üzerinden senkronize olur</string>
|
||||
<string name="settings_sync_wifi_only_ssid_off">Tüm WiFi bağlantıları kullanılabilir</string>
|
||||
<string name="settings_sync_wifi_only_ssid_message">Senkronizasyonu sadece bir WiFi ağına kısıtlamak için bu ağın adını (SSID) gir, veya tüm WiFi bağlantıları için boş bırak.</string>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Geçmiş olay zaman sınırı</string>
|
||||
<string name="settings_sync_time_range_past_none">Tüm olaylar senkronize edilecek</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="other">%d günden daha eski olaylar göz ardı edilecektir</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Bu sayıdan daha eski olan olaylar yok sayılacaktır (0 olabilir). Tüm olayları senkronize etmek için boş bırak.</string>
|
||||
<string name="settings_manage_calendar_colors">Takvim renklerini yönet</string>
|
||||
<string name="settings_manage_calendar_colors_on">Takvim renkleri DAVdroid tarafından yönetilmekte</string>
|
||||
<string name="settings_manage_calendar_colors_off">Takvim renkleri DAVdroid tarafından ayarlanmadı</string>
|
||||
<string name="settings_version_update">DAVdroid sürüm güncellemesi</string>
|
||||
<string name="settings_version_update_settings_updated">Dahili ayarlar güncellendi.</string>
|
||||
<string name="settings_version_update_install_hint">Sorunlar mı var? DAVdroid\'i kaldırıp, yeniden kurun.</string>
|
||||
<!--collection management-->
|
||||
<string name="create_addressbook">Rehber yarat</string>
|
||||
<string name="create_addressbook_display_name_hint">Benim Rehberim</string>
|
||||
<string name="create_calendar">CalDav koleksiyonu yarat</string>
|
||||
<string name="create_calendar_display_name_hint">Benim Takvimim</string>
|
||||
<string name="create_calendar_time_zone">Saat dilimi:</string>
|
||||
<string name="create_calendar_type">Koleksiyon Tipi:</string>
|
||||
<string name="create_calendar_type_only_events">Takvim (sadece olaylar)</string>
|
||||
<string name="create_calendar_type_only_tasks">İşler listesi (sadece işler)
|
||||
</string>
|
||||
<string name="create_calendar_type_events_and_tasks">Birleşik (olaylar ve işler)</string>
|
||||
<string name="create_collection_color">Bir koleksiyon rengi tanımla</string>
|
||||
<string name="create_collection_creating">Koleksiyon yaratılıyor</string>
|
||||
<string name="create_collection_display_name">Bu koleksiyonun gösterilen adı (başlığı):</string>
|
||||
<string name="create_collection_display_name_required">Başlık zorunlu</string>
|
||||
<string name="create_collection_description">Açıklama (isteğe bağlı):</string>
|
||||
<string name="create_collection_home_set">Ev seti:</string>
|
||||
<string name="create_collection_create">Yarat</string>
|
||||
<string name="delete_collection">Koleksiyonu sil</string>
|
||||
<string name="delete_collection_confirm_title">Emin misin?</string>
|
||||
<string name="delete_collection_confirm_warning">Bu koleksiyon (%s) ve tüm verisi sunucudan silinecek.</string>
|
||||
<string name="delete_collection_deleting_collection">Koleksiyon siliniyor</string>
|
||||
<!--ExceptionInfoFragment-->
|
||||
<string name="exception">Bir hata oluştu.</string>
|
||||
<string name="exception_httpexception">Bir HTTP hatası oluştu.</string>
|
||||
<string name="exception_ioexception">Bir I/O hatası oluştu.</string>
|
||||
<string name="exception_show_details">Detayları göster</string>
|
||||
<!--sync errors and DebugInfoActivity-->
|
||||
<string name="debug_info_title">Hata ayıklama bilgisi</string>
|
||||
<string name="sync_error_permissions">DAVdroid izinleri</string>
|
||||
<string name="sync_error_permissions_text">Ek izinler zorunludur</string>
|
||||
<string name="sync_error_calendar">Takvim senkronizasyonu başarısız (%s)</string>
|
||||
<string name="sync_error_contacts">Rehber senkronizasyonu başarısız (%s)</string>
|
||||
<string name="sync_error_tasks">İş senkronizasyonu başarısız (%s)</string>
|
||||
<string name="sync_error">%s yaparken hata</string>
|
||||
<string name="sync_error_http_dav">%s yaparken sunucu hatası</string>
|
||||
<string name="sync_error_local_storage">%s yaparken veritabanı hatası</string>
|
||||
<string-array name="sync_error_phases">
|
||||
<item>senkronizasyon hazırlanıyor</item>
|
||||
<item>yetkinlikler sorgulanıyor</item>
|
||||
<item>yerel olarak silinmiş kayıtlar işleniyor</item>
|
||||
<item>yaratılmış/değiştirilmiş kayıtlar hazırlanıyor</item>
|
||||
<item>yaratılmış/değiştirilmiş kayıtlar indiriliyor</item>
|
||||
<item>senkronizasyon durumu kontrol ediliyor</item>
|
||||
<item>yerel kayıtlar listeleniyor</item>
|
||||
<item>uzak kayıtlar listeleniyor</item>
|
||||
<item>yerel/uzak kayıtlar karşılaştırılıyor</item>
|
||||
<item>uzak kayıtlar indiriliyor</item>
|
||||
<item>senkronizasyon durumu kaydediliyor</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">Kullanıcı adı/parola yanlış</string>
|
||||
</resources>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<!--DavService-->
|
||||
<!--AppSettingsActivity-->
|
||||
<!--AccountActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_type_email">Увійти за допомогою електронної пошти</string>
|
||||
<string name="login_type_url">Увійти за допомогою URL та імені користувача</string>
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
<string name="startup_google_play_accounts_removed_message">在部分情况下,Play 商店的 DRM 可能会导致所有 DAVdroid 账户在设备重启或升级 DAVdroid 后消失。如果你遇到了该问题(并且只有这一问题),请从 Play 商店安装“DAVdroid JB Workaround”。</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">更多信息</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks 未安装</string>
|
||||
<string name="startup_opentasks_not_installed_message">OpenTasks 应用未安装或不可见,因此 DAVdroid 无法同步任务列表。请在安装 OpenTasks 后,重新安装 DAVdroid,再增加账户。</string>
|
||||
<string name="startup_opentasks_not_installed_message">未安装 OpenTasks 应用,故 DAVdroid 无法同步任务列表。</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">安装 OpenTasks 后,由于 Android 的限制,请重新安装 DAVdroid 并重新创建账户。</string>
|
||||
<string name="startup_opentasks_not_installed_install">安装 OpenTasks</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_terms">许可协议</string>
|
||||
@@ -75,6 +76,17 @@
|
||||
<string name="account_create_new_address_book">创建通讯录</string>
|
||||
<string name="account_refresh_calendar_list">刷新日历列表</string>
|
||||
<string name="account_create_new_calendar">创建日历</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">DAVdroid 权限</string>
|
||||
<string name="permissions_calendar">日历权限</string>
|
||||
<string name="permissions_calendar_details">要把 CalDAV 事件与本地日历同步,DAVdroid 需要日历权限。</string>
|
||||
<string name="permissions_calendar_request">请求日历权限</string>
|
||||
<string name="permissions_contacts">通讯录权限</string>
|
||||
<string name="permissions_contacts_details">要把 CardDAV 通讯录与本地通讯录同步,DAVdroid 需要通讯录权限。</string>
|
||||
<string name="permissions_contacts_request">请求通讯录权限</string>
|
||||
<string name="permissions_opentasks">OpenTasks 权限</string>
|
||||
<string name="permissions_opentasks_details">要把 CalDAV 任务与本地任务列表同步,DAVdroid 需要访问 OpenTasks。</string>
|
||||
<string name="permissions_opentasks_request">请求 OpenTasks 权限</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">增加账户</string>
|
||||
<string name="login_type_email">使用邮箱地址登录</string>
|
||||
@@ -94,6 +106,7 @@
|
||||
<string name="login_create_account">创建账户</string>
|
||||
<string name="login_account_name">账户显示名</string>
|
||||
<string name="login_account_name_info">请使用你的邮箱地址作为帐户名,因为 Android 会将你创建的日历事件的创建者项设置为帐户名。你不能拥有多个帐户名相同的账户。</string>
|
||||
<string name="login_account_contact_group_method">联系人分组方式</string>
|
||||
<string name="login_account_name_required">请输入账户名</string>
|
||||
<string name="login_account_not_created">账户无法创建</string>
|
||||
<string name="login_configuration_detection">正在配置</string>
|
||||
@@ -138,12 +151,36 @@
|
||||
<item>每 4 小时</item>
|
||||
<item>每 24 小时</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">只在 WiFi 下同步</string>
|
||||
<string name="settings_sync_wifi_only_on">同步只在 WiFi 连接下进行</string>
|
||||
<string name="settings_sync_wifi_only_off">同步不受数据连接类型限制</string>
|
||||
<string name="settings_sync_wifi_only_ssid">WiFi SSID 限制</string>
|
||||
<string name="settings_sync_wifi_only_ssid_on">同步只在 %s 网络下进行</string>
|
||||
<string name="settings_sync_wifi_only_ssid_off">任何 WiFi 网络下均会同步</string>
|
||||
<string name="settings_sync_wifi_only_ssid_message">输入 WiFi 网络的名称 (SSID) ,即可限制同步只在此网络下进行。留空则不限制。</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">联系人分组方式</string>
|
||||
<string-array name="settings_contact_group_method_values">
|
||||
<item>GROUP_VCARDS</item>
|
||||
<item>CATEGORIES</item>
|
||||
</string-array>
|
||||
<string-array name="settings_contact_group_method_entries">
|
||||
<item>分为不同的 VCards</item>
|
||||
<item>每个联系人的分类</item>
|
||||
</string-array>
|
||||
<string name="settings_rfc6868_for_vcards">为 VCards 启用 RFC6868</string>
|
||||
<string name="settings_rfc6868_for_vcards_on">属性值中可使用双引号</string>
|
||||
<string name="settings_rfc6868_for_vcards_off">属性值中不可使用双引号</string>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">旧日程时间限制</string>
|
||||
<string name="settings_sync_time_range_past_none">同步所有日程</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
<item quantity="other">%d 天前的日程不会被同步</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">超过这个数字的天数的旧日程将会被忽略(可以为 0)。留空则同步所有日程。</string>
|
||||
<string name="settings_manage_calendar_colors">管理日历颜色</string>
|
||||
<string name="settings_manage_calendar_colors_on">日历颜色由 DAVdroid 设置</string>
|
||||
<string name="settings_manage_calendar_colors_off">日历颜色不由 DAVdroid 设置</string>
|
||||
<string name="settings_version_update">DAVdroid 版本升级</string>
|
||||
<string name="settings_version_update_settings_updated">应用设置已更新。</string>
|
||||
<string name="settings_version_update_install_hint">出现问题了?请卸载 DAVdroid,再重新安装。</string>
|
||||
@@ -175,6 +212,8 @@
|
||||
<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>
|
||||
@@ -192,6 +231,7 @@
|
||||
<item>检查远程数据</item>
|
||||
<item>比较本地和远程数据</item>
|
||||
<item>下载远程数据</item>
|
||||
<item>预处理</item>
|
||||
<item>保存同步状态</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">用户名或密码错误</string>
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
<string name="startup_google_play_accounts_removed_message">Under certain conditions, Play Store DRM may cause all DAVdroid accounts to be gone after a reboot or after upgrading DAVdroid. If you\'re affected by this problem (and only then), please install \"DAVdroid JB Workaround\" from Play Store.</string>
|
||||
<string name="startup_google_play_accounts_removed_more_info">More information</string>
|
||||
<string name="startup_opentasks_not_installed">OpenTasks not installed</string>
|
||||
<string name="startup_opentasks_not_installed_message">The OpenTasks app is not installed or not accessible, so DAVdroid won\'t be able to synchronize task lists. Re-install DAVdroid and then add your accounts again after installing OpenTasks.</string>
|
||||
<string name="startup_opentasks_not_installed_message">The OpenTasks app is not available, so DAVdroid won\'t be able to synchronize task lists.</string>
|
||||
<string name="startup_opentasks_reinstall_davdroid">After installing OpenTasks, you have to RE-INSTALL DAVdroid and add your accounts again (Android bug).</string>
|
||||
<string name="startup_opentasks_not_installed_install">Install OpenTasks</string>
|
||||
|
||||
<!-- AboutActivity -->
|
||||
@@ -94,6 +95,18 @@
|
||||
<string name="account_refresh_calendar_list">Refresh calendar list</string>
|
||||
<string name="account_create_new_calendar">Create new calendar</string>
|
||||
|
||||
<!-- PermissionsActivity -->
|
||||
<string name="permissions_title">DAVdroid permissions</string>
|
||||
<string name="permissions_calendar">Calendar permissions</string>
|
||||
<string name="permissions_calendar_details">To synchronize CalDAV events with your local calendars, DAVdroid needs to access your calendars.</string>
|
||||
<string name="permissions_calendar_request">Request calendar permissions</string>
|
||||
<string name="permissions_contacts">Contacts permissions</string>
|
||||
<string name="permissions_contacts_details">To synchronize CardDAV address books with your local contacts, DAVdroid needs to access your contacts.</string>
|
||||
<string name="permissions_contacts_request">Request contacts permissions</string>
|
||||
<string name="permissions_opentasks">OpenTasks permissions</string>
|
||||
<string name="permissions_opentasks_details">To synchronize CalDAV tasks with your local task lists, DAVdroid needs to access OpenTasks.</string>
|
||||
<string name="permissions_opentasks_request">Request OpenTasks permissions</string>
|
||||
|
||||
<!-- AddAccountActivity -->
|
||||
<string name="login_title">Add account</string>
|
||||
<string name="login_type_email">Login with email address</string>
|
||||
@@ -113,6 +126,7 @@
|
||||
<string name="login_create_account">Create account</string>
|
||||
<string name="login_account_name">Account name</string>
|
||||
<string name="login_account_name_info">Use your email address as account name because Android will use the account name as ORGANIZER field for events you create. You can\'t have two accounts with the same name.</string>
|
||||
<string name="login_account_contact_group_method">Contact group method:</string>
|
||||
<string name="login_account_name_required">Account name required</string>
|
||||
<string name="login_account_not_created">Account could not be created</string>
|
||||
|
||||
@@ -159,6 +173,27 @@
|
||||
<item>Every 4 hours</item>
|
||||
<item>Once a day</item>
|
||||
</string-array>
|
||||
<string name="settings_sync_wifi_only">Sync over WiFi only</string>
|
||||
<string name="settings_sync_wifi_only_on">Synchronization is restricted to WiFi connections</string>
|
||||
<string name="settings_sync_wifi_only_off">Connection type is not taken into consideration</string>
|
||||
<string name="settings_sync_wifi_only_ssid">WiFi SSID restriction</string>
|
||||
<string name="settings_sync_wifi_only_ssid_on">Will only synchronize over %s</string>
|
||||
<string name="settings_sync_wifi_only_ssid_off">All WiFi connections may be used</string>
|
||||
<string name="settings_sync_wifi_only_ssid_message">Enter the name of a WiFi network (SSID) to restrict synchronization to this network, or leave blank for all WiFi connections.</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">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>Groups are separate VCards</item>
|
||||
<item>Groups are per-contact categories</item>
|
||||
</string-array>
|
||||
<string name="settings_rfc6868_for_vcards">Use RFC6868 for VCards</string>
|
||||
<string name="settings_rfc6868_for_vcards_on">Double quotes can be used in parameter values</string>
|
||||
<string name="settings_rfc6868_for_vcards_off">Double quotes can\'t be used in parameter values</string>
|
||||
<string name="settings_caldav">CalDAV</string>
|
||||
<string name="settings_sync_time_range_past">Past event time limit</string>
|
||||
<string name="settings_sync_time_range_past_none">All events will be synchronized</string>
|
||||
<plurals name="settings_sync_time_range_past_days">
|
||||
@@ -166,6 +201,9 @@
|
||||
<item quantity="other">Events more than %d days in the past will be ignored</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Events which are more than this number of days in the past will be ignored (may be 0). Leave blank to synchronize all events.</string>
|
||||
<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_version_update">DAVdroid version update</string>
|
||||
<string name="settings_version_update_settings_updated">Internal settings have been updated.</string>
|
||||
<string name="settings_version_update_install_hint">Problems? Uninstall DAVdroid, then re-install.</string>
|
||||
@@ -200,6 +238,8 @@
|
||||
|
||||
<!-- sync errors and DebugInfoActivity -->
|
||||
<string name="debug_info_title">Debug info</string>
|
||||
<string name="sync_error_permissions">DAVdroid permissions</string>
|
||||
<string name="sync_error_permissions_text">Additional permissions required</string>
|
||||
<string name="sync_error_calendar">Calendar synchronization failed (%s)</string>
|
||||
<string name="sync_error_contacts">Address book synchronization failed (%s)</string>
|
||||
<string name="sync_error_tasks">Task synchronization failed (%s)</string>
|
||||
@@ -217,6 +257,7 @@
|
||||
<item>listing remote entries</item>
|
||||
<item>comparing local/remote entries</item>
|
||||
<item>downloading remote entries</item>
|
||||
<item>post-processing</item>
|
||||
<item>saving sync state</item>
|
||||
</string-array>
|
||||
<string name="sync_error_unauthorized">User name/password wrong</string>
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
<color name="very_light_grey">#ebebeb</color>
|
||||
<color name="white">#ffffff</color>
|
||||
|
||||
<!-- preference-v14 uses this color for categories text -->
|
||||
<color name="preference_fallback_accent_color">@color/orangeA700</color>
|
||||
|
||||
|
||||
<!-- app theme -->
|
||||
|
||||
@@ -33,7 +36,7 @@
|
||||
<item name="colorAccent">@color/orangeA700</item>
|
||||
<item name="android:textColorPrimary">@drawable/text_color</item>
|
||||
<item name="android:textColorSecondary">@color/grey</item>
|
||||
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
|
||||
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.NoActionBar">
|
||||
@@ -81,13 +84,8 @@
|
||||
|
||||
<!-- widgets -->
|
||||
|
||||
<style name="chip">
|
||||
<item name="android:padding">4dp</item>
|
||||
<item name="android:background">@drawable/chip_bg</item>
|
||||
</style>
|
||||
|
||||
<style name="tablayout">
|
||||
<item name="colorPrimary">@color/orangeA700</item>
|
||||
<style name="tablayout" parent="Base.Widget.Design.TabLayout">
|
||||
<item name="android:background">@color/light_green700</item>
|
||||
</style>
|
||||
|
||||
<style name="toolbar_theme">
|
||||
@@ -106,4 +104,4 @@
|
||||
<item name="android:textColor">@color/orangeA700</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -57,13 +57,57 @@
|
||||
android:entries="@array/settings_sync_interval_names"
|
||||
android:entryValues="@array/settings_sync_interval_seconds" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="sync_wifi_only"
|
||||
android:persistent="false"
|
||||
android:title="@string/settings_sync_wifi_only"
|
||||
android:summaryOn="@string/settings_sync_wifi_only_on"
|
||||
android:summaryOff="@string/settings_sync_wifi_only_off"
|
||||
/>
|
||||
|
||||
<EditTextPreference
|
||||
android:key="caldav_time_range_past_days"
|
||||
android:key="sync_wifi_only_ssid"
|
||||
android:dependency="sync_wifi_only"
|
||||
android:persistent="false"
|
||||
android:title="@string/settings_sync_wifi_only_ssid"
|
||||
android:dialogMessage="@string/settings_sync_wifi_only_ssid_message"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/settings_carddav">
|
||||
|
||||
<ListPreference
|
||||
android:key="contact_group_method"
|
||||
android:persistent="false"
|
||||
android:title="@string/settings_contact_group_method"
|
||||
android:entries="@array/settings_contact_group_method_entries"
|
||||
android:entryValues="@array/settings_contact_group_method_values"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="vcard_rfc6868"
|
||||
android:persistent="false"
|
||||
android:title="@string/settings_rfc6868_for_vcards"
|
||||
android:summaryOn="@string/settings_rfc6868_for_vcards_on"
|
||||
android:summaryOff="@string/settings_rfc6868_for_vcards_off"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/settings_caldav">
|
||||
|
||||
<EditTextPreference
|
||||
android:key="time_range_past_days"
|
||||
android:persistent="false"
|
||||
android:title="@string/settings_sync_time_range_past"
|
||||
android:dialogMessage="@string/settings_sync_time_range_past_message"
|
||||
android:inputType="number"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="manage_calendar_colors"
|
||||
android:persistent="false"
|
||||
android:title="@string/settings_manage_calendar_colors"
|
||||
android:summaryOn="@string/settings_manage_calendar_colors_on"
|
||||
android:summaryOff="@string/settings_manage_calendar_colors_off"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
41
app/src/test/java/at/bitfire/davdroid/ArrayUtilsTest.java
Normal file
41
app/src/test/java/at/bitfire/davdroid/ArrayUtilsTest.java
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright © 2013 – 2015 Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
package at.bitfire.davdroid;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
public class ArrayUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testPartition() {
|
||||
// n == 0
|
||||
Assert.assertTrue(Arrays.deepEquals(
|
||||
new Long[0][0],
|
||||
ArrayUtils.partition(new Long[] {}, 5)));
|
||||
|
||||
// n < max
|
||||
Assert.assertTrue(Arrays.deepEquals(
|
||||
new Long[][] { { 1l, 2l } },
|
||||
ArrayUtils.partition(new Long[] { 1l, 2l }, 5)));
|
||||
|
||||
// n == max
|
||||
Assert.assertTrue(Arrays.deepEquals(
|
||||
new Long[][] { { 1l, 2l }, { 3l, 4l } },
|
||||
ArrayUtils.partition(new Long[] { 1l, 2l, 3l, 4l }, 2)));
|
||||
|
||||
// n > max
|
||||
Assert.assertTrue(Arrays.deepEquals(
|
||||
new Long[][] { { 1l, 2l, 3l, 4l, 5l }, { 6l, 7l, 8l, 9l, 10l }, { 11l } },
|
||||
ArrayUtils.partition(new Long[] { 1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l, 9l, 10l, 11l }, 5)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
package at.bitfire.davdroid;
|
||||
|
||||
import android.test.InstrumentationTestCase;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
@@ -19,7 +19,7 @@ import okhttp3.Request;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
|
||||
public class HttpClientTest extends InstrumentationTestCase {
|
||||
public class HttpClientTest extends TestCase {
|
||||
|
||||
MockWebServer server;
|
||||
OkHttpClient httpClient;
|
||||
@@ -29,7 +29,7 @@ public class HttpClientTest extends InstrumentationTestCase {
|
||||
httpClient = HttpClient.create();
|
||||
|
||||
server = new MockWebServer();
|
||||
server.start();
|
||||
server.start(30000);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -38,12 +38,13 @@ public class HttpClientTest extends InstrumentationTestCase {
|
||||
}
|
||||
|
||||
public void testCookies() throws IOException, InterruptedException, URISyntaxException {
|
||||
HttpUrl url = server.url("/");
|
||||
HttpUrl url = server.url("/test");
|
||||
|
||||
// set cookie in first response
|
||||
// set cookie for root path (/) and /test path in first response
|
||||
server.enqueue(new MockResponse()
|
||||
.setResponseCode(200)
|
||||
.setHeader("Set-Cookie", "theme=light; path=/")
|
||||
.addHeader("Set-Cookie", "cookie1=1; path=/")
|
||||
.addHeader("Set-Cookie", "cookie2=2")
|
||||
.setBody("Cookie set"));
|
||||
httpClient.newCall(new Request.Builder()
|
||||
.get().url(url)
|
||||
@@ -51,14 +52,22 @@ public class HttpClientTest extends InstrumentationTestCase {
|
||||
assertNull(server.takeRequest().getHeader("Cookie"));
|
||||
|
||||
// cookie should be sent with second request
|
||||
// second response lets first cookie expire and overwrites second cookie
|
||||
server.enqueue(new MockResponse()
|
||||
.addHeader("Set-Cookie", "cookie1=1a; path=/; Max-Age=0")
|
||||
.addHeader("Set-Cookie", "cookie2=2a")
|
||||
.setResponseCode(200));
|
||||
httpClient.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()
|
||||
.get().url(url)
|
||||
.build()).execute();
|
||||
//assertEquals("theme=light", server.takeRequest().getHeader("Cookie"));
|
||||
|
||||
// doesn't work for URLs with ports, see https://code.google.com/p/android/issues/detail?id=193475
|
||||
assertEquals("cookie2=2a", server.takeRequest().getHeader("Cookie"));
|
||||
}
|
||||
|
||||
}
|
||||
26
app/src/test/java/at/bitfire/davdroid/TestDavUtils.java
Normal file
26
app/src/test/java/at/bitfire/davdroid/TestDavUtils.java
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestDavUtils {
|
||||
|
||||
private static final String exampleURL = "http://example.com/";
|
||||
|
||||
@Test
|
||||
public void testLastSegmentOfUrl() {
|
||||
Assert.assertEquals("/", DavUtils.lastSegmentOfUrl(exampleURL));
|
||||
Assert.assertEquals("dir", DavUtils.lastSegmentOfUrl(exampleURL + "dir"));
|
||||
Assert.assertEquals("dir", DavUtils.lastSegmentOfUrl(exampleURL + "dir/"));
|
||||
Assert.assertEquals("file.html", DavUtils.lastSegmentOfUrl(exampleURL + "dir/file.html"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,7 +12,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.5.0+'
|
||||
classpath 'com.android.tools.build:gradle:2.1.0'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,3 +24,5 @@ allprojects {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'android-reporting'
|
||||
Submodule dav4android updated: 2d01d3b45d...d5487a1623
1
doc/.gitignore
vendored
Normal file
1
doc/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
pr/
|
||||
BIN
doc/DAVdroid-Linuxwochen-2016.odp
Normal file
BIN
doc/DAVdroid-Linuxwochen-2016.odp
Normal file
Binary file not shown.
1
gradle.properties
Normal file
1
gradle.properties
Normal file
@@ -0,0 +1 @@
|
||||
org.gradle.jvmargs=-Xmx2048M
|
||||
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
#Fri Oct 30 11:00:03 CET 2015
|
||||
#Sat Apr 09 22:18:11 CEST 2016
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
|
||||
|
||||
Submodule ical4android updated: 19ef2603ed...ee884d351b
2
run-tests-connected.sh
Executable file
2
run-tests-connected.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
./gradlew -i check mergeAndroidReports --continue
|
||||
3
scripts/fetch-db.sh
Executable file
3
scripts/fetch-db.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
cd ~/tmp
|
||||
adb pull /data/data/com.android.providers.contacts/databases/contacts2.db
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
declare -A android
|
||||
android=([ca]=ca [cs]=cs [da]=da [de]=de [es]=es [fr]=fr [hu]=hu [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 [pl]=pl [pt]=pt [pt_BR]=pt-rBR [ru]=ru [sr]=sr [tr_TR]=tr-rTR [uk]=uk [zh_CN]=zh-rCN)
|
||||
|
||||
for lang in ${!android[@]}
|
||||
do
|
||||
|
||||
13
scripts/gen-contacts.rb
Executable file
13
scripts/gen-contacts.rb
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/ruby
|
||||
|
||||
File.open("contacts.vcf", "w") do |f|
|
||||
for i in 1..600 do
|
||||
f.puts "BEGIN:VCARD"
|
||||
f.puts "VERSION:3.0"
|
||||
f.puts "FN:Kontakt Nr. #{i}"
|
||||
f.puts "N:Kontakt Nr. #{i}"
|
||||
f.puts "EMAIL:#{i}@google-god.com"
|
||||
f.puts "PHONE:#{i}#{i}#{i}"
|
||||
f.puts "END:VCARD"
|
||||
end
|
||||
end
|
||||
Submodule vcard4android updated: 4eab186a21...3a6692f096
Reference in New Issue
Block a user