mirror of
https://github.com/etesync/android
synced 2024-11-22 07:58:09 +00:00
New version: 0.6.12
* if well-known URI detection fails with I/O errors, ignore it instead of aborting * if current-user-principal is not supported by the server, use the user-given URL is the principal URL * determine availability of CalDAV/CardDAV by whether a calendar/address-book home set is available * if DAV:displayname is not available for a collection, use its URL path instead * remove SSLPeerUnverifiedException handling (Apache HttpClient doesn't use this exception) * use Depth: 1 (instead of Depth: 0) for multi-get REPORT requests * don't display address books in "Select collections" UI if CardDAV is not available * don't display calendars in "Select collections" UI if CalDAV is not available * version bump to 0.6.12
This commit is contained in:
parent
6c998c31c3
commit
aeca582a7c
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="at.bitfire.davdroid"
|
||||
android:versionCode="53" android:versionName="0.6.11"
|
||||
android:versionCode="54" android:versionName="0.6.12"
|
||||
android:installLocation="internalOnly">
|
||||
|
||||
<uses-sdk
|
||||
|
@ -9,7 +9,7 @@ package at.bitfire.davdroid;
|
||||
|
||||
public class Constants {
|
||||
public static final String
|
||||
APP_VERSION = "0.6.11",
|
||||
APP_VERSION = "0.6.12",
|
||||
ACCOUNT_TYPE = "bitfire.at.davdroid",
|
||||
WEB_URL_HELP = "https://davdroid.bitfire.at/configuration?pk_campaign=davdroid-app",
|
||||
WEB_URL_VIEW_LOGS = "https://github.com/bitfireAT/davdroid/wiki/How-to-view-the-logs";
|
||||
|
@ -59,90 +59,94 @@ public class DavResourceFinder implements Closeable {
|
||||
public void findResources(ServerInfo serverInfo) throws URISyntaxException, DavException, HttpException, IOException {
|
||||
// CardDAV
|
||||
WebDavResource principal = getCurrentUserPrincipal(serverInfo, "carddav");
|
||||
if (principal != null) {
|
||||
serverInfo.setCardDAV(true);
|
||||
|
||||
URI uriAddressBookHomeSet = null;
|
||||
try {
|
||||
principal.propfind(Mode.HOME_SETS);
|
||||
URI uriAddressBookHomeSet = principal.getAddressbookHomeSet();
|
||||
if (uriAddressBookHomeSet != null) {
|
||||
Log.i(TAG, "Found address book home set: " + uriAddressBookHomeSet);
|
||||
|
||||
WebDavResource homeSetAddressBooks = new WebDavResource(principal, uriAddressBookHomeSet);
|
||||
if (checkHomesetCapabilities(homeSetAddressBooks, "addressbook")) {
|
||||
homeSetAddressBooks.propfind(Mode.CARDDAV_COLLECTIONS);
|
||||
|
||||
List<ServerInfo.ResourceInfo> addressBooks = new LinkedList<>();
|
||||
if (homeSetAddressBooks.getMembers() != null)
|
||||
for (WebDavResource resource : homeSetAddressBooks.getMembers())
|
||||
if (resource.isAddressBook()) {
|
||||
Log.i(TAG, "Found address book: " + resource.getLocation().getPath());
|
||||
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
|
||||
ServerInfo.ResourceInfo.Type.ADDRESS_BOOK,
|
||||
resource.isReadOnly(),
|
||||
resource.getLocation().toString(),
|
||||
resource.getDisplayName(),
|
||||
resource.getDescription(), resource.getColor()
|
||||
);
|
||||
|
||||
VCardVersion version = resource.getVCardVersion();
|
||||
if (version == null)
|
||||
version = VCardVersion.V3_0; // VCard 3.0 MUST be supported
|
||||
info.setVCardVersion(version);
|
||||
|
||||
addressBooks.add(info);
|
||||
}
|
||||
serverInfo.setAddressBooks(addressBooks);
|
||||
} else
|
||||
Log.w(TAG, "Found address-book home set, but it doesn't advertise CardDAV support");
|
||||
}
|
||||
uriAddressBookHomeSet = principal.getAddressbookHomeSet();
|
||||
} catch (Exception e) {
|
||||
Log.i(TAG, "Couldn't find address-book home set", e);
|
||||
}
|
||||
|
||||
if (uriAddressBookHomeSet != null) {
|
||||
serverInfo.setCardDAV(true);
|
||||
Log.i(TAG, "Found address-book home set: " + uriAddressBookHomeSet);
|
||||
|
||||
WebDavResource homeSetAddressBooks = new WebDavResource(principal, uriAddressBookHomeSet);
|
||||
if (checkHomesetCapabilities(homeSetAddressBooks, "addressbook")) {
|
||||
homeSetAddressBooks.propfind(Mode.CARDDAV_COLLECTIONS);
|
||||
|
||||
List<ServerInfo.ResourceInfo> addressBooks = new LinkedList<>();
|
||||
if (homeSetAddressBooks.getMembers() != null)
|
||||
for (WebDavResource resource : homeSetAddressBooks.getMembers())
|
||||
if (resource.isAddressBook()) {
|
||||
Log.i(TAG, "Found address book: " + resource.getLocation().getPath());
|
||||
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
|
||||
ServerInfo.ResourceInfo.Type.ADDRESS_BOOK,
|
||||
resource.isReadOnly(),
|
||||
resource.getLocation().toString(),
|
||||
resource.getDisplayName(),
|
||||
resource.getDescription(), resource.getColor()
|
||||
);
|
||||
|
||||
VCardVersion version = resource.getVCardVersion();
|
||||
if (version == null)
|
||||
version = VCardVersion.V3_0; // VCard 3.0 MUST be supported
|
||||
info.setVCardVersion(version);
|
||||
|
||||
addressBooks.add(info);
|
||||
}
|
||||
serverInfo.setAddressBooks(addressBooks);
|
||||
} else
|
||||
Log.w(TAG, "Found address-book home set, but it doesn't advertise CardDAV support");
|
||||
}
|
||||
|
||||
// CalDAV
|
||||
principal = getCurrentUserPrincipal(serverInfo, "caldav");
|
||||
if (principal != null) {
|
||||
serverInfo.setCalDAV(true);
|
||||
|
||||
URI uriCalendarHomeSet = null;
|
||||
try {
|
||||
principal.propfind(Mode.HOME_SETS);
|
||||
URI uriCalendarHomeSet = principal.getCalendarHomeSet();
|
||||
if (uriCalendarHomeSet != null) {
|
||||
Log.i(TAG, "Found calendar home set: " + uriCalendarHomeSet);
|
||||
|
||||
WebDavResource homeSetCalendars = new WebDavResource(principal, uriCalendarHomeSet);
|
||||
if (checkHomesetCapabilities(homeSetCalendars, "calendar-access")) {
|
||||
homeSetCalendars.propfind(Mode.CALDAV_COLLECTIONS);
|
||||
|
||||
List<ServerInfo.ResourceInfo> calendars = new LinkedList<>();
|
||||
if (homeSetCalendars.getMembers() != null)
|
||||
for (WebDavResource resource : homeSetCalendars.getMembers())
|
||||
if (resource.isCalendar()) {
|
||||
Log.i(TAG, "Found calendar: " + resource.getLocation().getPath());
|
||||
if (resource.getSupportedComponents() != null) {
|
||||
// CALDAV:supported-calendar-component-set available
|
||||
boolean supportsEvents = false;
|
||||
for (String supportedComponent : resource.getSupportedComponents())
|
||||
if (supportedComponent.equalsIgnoreCase("VEVENT"))
|
||||
supportsEvents = true;
|
||||
if (!supportsEvents) { // ignore collections without VEVENT support
|
||||
Log.i(TAG, "Ignoring this calendar because of missing VEVENT support");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
|
||||
ServerInfo.ResourceInfo.Type.CALENDAR,
|
||||
resource.isReadOnly(),
|
||||
resource.getLocation().toString(),
|
||||
resource.getDisplayName(),
|
||||
resource.getDescription(), resource.getColor()
|
||||
);
|
||||
info.setTimezone(resource.getTimezone());
|
||||
calendars.add(info);
|
||||
}
|
||||
serverInfo.setCalendars(calendars);
|
||||
} else
|
||||
Log.w(TAG, "Found calendar home set, but it doesn't advertise CalDAV support");
|
||||
}
|
||||
uriCalendarHomeSet = principal.getCalendarHomeSet();
|
||||
} catch(Exception e) {
|
||||
Log.i(TAG, "Couldn't find calendar home set", e);
|
||||
}
|
||||
|
||||
if (uriCalendarHomeSet != null) {
|
||||
serverInfo.setCalDAV(true);
|
||||
Log.i(TAG, "Found calendar home set: " + uriCalendarHomeSet);
|
||||
|
||||
WebDavResource homeSetCalendars = new WebDavResource(principal, uriCalendarHomeSet);
|
||||
if (checkHomesetCapabilities(homeSetCalendars, "calendar-access")) {
|
||||
homeSetCalendars.propfind(Mode.CALDAV_COLLECTIONS);
|
||||
|
||||
List<ServerInfo.ResourceInfo> calendars = new LinkedList<>();
|
||||
if (homeSetCalendars.getMembers() != null)
|
||||
for (WebDavResource resource : homeSetCalendars.getMembers())
|
||||
if (resource.isCalendar()) {
|
||||
Log.i(TAG, "Found calendar: " + resource.getLocation().getPath());
|
||||
if (resource.getSupportedComponents() != null) {
|
||||
// CALDAV:supported-calendar-component-set available
|
||||
boolean supportsEvents = false;
|
||||
for (String supportedComponent : resource.getSupportedComponents())
|
||||
if (supportedComponent.equalsIgnoreCase("VEVENT"))
|
||||
supportsEvents = true;
|
||||
if (!supportsEvents) { // ignore collections without VEVENT support
|
||||
Log.i(TAG, "Ignoring this calendar because of missing VEVENT support");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
|
||||
ServerInfo.ResourceInfo.Type.CALENDAR,
|
||||
resource.isReadOnly(),
|
||||
resource.getLocation().toString(),
|
||||
resource.getDisplayName(),
|
||||
resource.getDescription(), resource.getColor()
|
||||
);
|
||||
info.setTimezone(resource.getTimezone());
|
||||
calendars.add(info);
|
||||
}
|
||||
serverInfo.setCalendars(calendars);
|
||||
} else
|
||||
Log.w(TAG, "Found calendar home set, but it doesn't advertise CalDAV support");
|
||||
}
|
||||
|
||||
if (!serverInfo.isCalDAV() && !serverInfo.isCardDAV())
|
||||
throw new DavIncapableException(context.getString(R.string.setup_neither_caldav_nor_carddav));
|
||||
|
||||
@ -251,11 +255,13 @@ public class DavResourceFinder implements Closeable {
|
||||
Log.w(TAG, "Not authorized for well-known " + serviceName + " service detection", e);
|
||||
throw e;
|
||||
} catch (URISyntaxException e) {
|
||||
Log.w(TAG, "Well-known" + serviceName + " service detection failed because of invalid URIs", e);
|
||||
Log.e(TAG, "Well-known" + serviceName + " service detection failed because of invalid URIs", e);
|
||||
} catch (HttpException e) {
|
||||
Log.d(TAG, "Well-known " + serviceName + " service detection failed with HTTP error", e);
|
||||
} catch (DavException e) {
|
||||
Log.w(TAG, "Well-known " + serviceName + " service detection failed with unexpected DAV response", e);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Well-known " + serviceName + " service detection failed with I/O error", e);
|
||||
}
|
||||
|
||||
// fall back to user-given initial context path
|
||||
@ -271,7 +277,9 @@ public class DavResourceFinder implements Closeable {
|
||||
} catch (DavException e) {
|
||||
Log.e(TAG, "DAV error when querying principal", e);
|
||||
}
|
||||
Log.i(TAG, "Couldn't find current-user-principal for service " + serviceName);
|
||||
|
||||
Log.i(TAG, "Couldn't find current-user-principal for service " + serviceName + ", assuming user-given path is principal path");
|
||||
return base;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -51,8 +51,10 @@ import net.fortuna.ical4j.model.property.Status;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.text.ParseException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -119,7 +121,7 @@ public class LocalCalendar extends LocalCollection<Event> {
|
||||
color = (color_alpha << 24) | color_rgb;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(Calendars.ACCOUNT_NAME, account.name);
|
||||
values.put(Calendars.ACCOUNT_TYPE, account.type);
|
||||
|
@ -8,6 +8,7 @@
|
||||
package at.bitfire.davdroid.resource;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -68,5 +69,18 @@ public class ServerInfo implements Serializable {
|
||||
VCardVersion vCardVersion;
|
||||
|
||||
String timezone;
|
||||
|
||||
|
||||
public String getTitle() {
|
||||
if (title == null) {
|
||||
try {
|
||||
java.net.URL url = new java.net.URL(URL);
|
||||
return url.getPath();
|
||||
} catch (MalformedURLException e) {
|
||||
return URL;
|
||||
}
|
||||
} else
|
||||
return title;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -125,8 +125,6 @@ public class QueryServerDialogFragment extends DialogFragment implements LoaderC
|
||||
// overwrite by more specific message, if possible
|
||||
if (ExceptionUtils.indexOfType(e, CertPathValidatorException.class) != -1)
|
||||
serverInfo.setErrorMessage(getContext().getString(R.string.exception_cert_path_validation, e.getMessage()));
|
||||
else if (ExceptionUtils.indexOfType(e, SSLPeerUnverifiedException.class) != -1)
|
||||
serverInfo.setErrorMessage(getContext().getString(R.string.exception_peer_unverified, e.getMessage()));
|
||||
} catch (HttpException e) {
|
||||
Log.e(TAG, "HTTP error while querying server info", e);
|
||||
serverInfo.setErrorMessage(getContext().getString(R.string.exception_http, e.getLocalizedMessage()));
|
||||
|
@ -30,7 +30,7 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
|
||||
|
||||
protected Context context;
|
||||
protected ServerInfo serverInfo;
|
||||
@Getter protected int nAddressBooks, nCalendars;
|
||||
@Getter protected int nAddressBooks, nAddressbookHeadings, nCalendars, nCalendarHeadings;
|
||||
|
||||
|
||||
public SelectCollectionsAdapter(Context context, ServerInfo serverInfo) {
|
||||
@ -38,7 +38,9 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
|
||||
|
||||
this.serverInfo = serverInfo;
|
||||
nAddressBooks = (serverInfo.getAddressBooks() == null) ? 0 : serverInfo.getAddressBooks().size();
|
||||
nAddressbookHeadings = (nAddressBooks == 0) ? 0 : 1;
|
||||
nCalendars = (serverInfo.getCalendars() == null) ? 0 : serverInfo.getCalendars().size();
|
||||
nCalendarHeadings = (nCalendars == 0) ? 0 : 1;
|
||||
}
|
||||
|
||||
|
||||
@ -46,15 +48,17 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return nAddressBooks + nCalendars + 2;
|
||||
return nAddressbookHeadings + nAddressBooks + nCalendarHeadings + nCalendars;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
if (position > 0 && position <= nAddressBooks)
|
||||
return serverInfo.getAddressBooks().get(position - 1);
|
||||
else if (position > nAddressBooks + 1)
|
||||
return serverInfo.getCalendars().get(position - nAddressBooks - 2);
|
||||
if (position >= nAddressbookHeadings &&
|
||||
position < (nAddressbookHeadings + nAddressBooks))
|
||||
return serverInfo.getAddressBooks().get(position - nAddressbookHeadings);
|
||||
else if (position >= (nAddressbookHeadings + nAddressBooks + nCalendarHeadings) &&
|
||||
(position < (nAddressbookHeadings + nAddressBooks + nCalendarHeadings + nCalendars)))
|
||||
return serverInfo.getCalendars().get(position - (nAddressbookHeadings + nAddressBooks + nCalendarHeadings));
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -78,13 +82,13 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (position == 0)
|
||||
if ((nAddressbookHeadings != 0) && (position == 0))
|
||||
return TYPE_ADDRESS_BOOKS_HEADING;
|
||||
else if (position <= nAddressBooks)
|
||||
else if ((nAddressbookHeadings != 0) && (position > 0) && (position < nAddressbookHeadings + nAddressBooks))
|
||||
return TYPE_ADDRESS_BOOKS_ROW;
|
||||
else if (position == nAddressBooks + 1)
|
||||
else if ((nCalendars != 0) && (position == nAddressbookHeadings + nAddressBooks))
|
||||
return TYPE_CALENDARS_HEADING;
|
||||
else if (position <= nAddressBooks + nCalendars + 1)
|
||||
else if ((nCalendars != 0) && (position > nAddressbookHeadings + nAddressBooks) && (position < nAddressbookHeadings + nAddressBooks + nCalendarHeadings + nCalendars))
|
||||
return TYPE_CALENDARS_ROW;
|
||||
else
|
||||
return IGNORE_ITEM_VIEW_TYPE;
|
||||
@ -133,11 +137,7 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
|
||||
view.setCompoundDrawablePadding(10);
|
||||
|
||||
// set text
|
||||
String title = info.getTitle();
|
||||
if (title == null) // unnamed collection
|
||||
title = context.getString((info.getType() == Type.ADDRESS_BOOK) ?
|
||||
R.string.setup_address_book : R.string.setup_calendar);
|
||||
title = "<b>" + title + "</b>";
|
||||
String title = "<b>" + info.getTitle() + "</b>";
|
||||
if (info.isReadOnly())
|
||||
title = title + " (" + context.getString(R.string.setup_read_only) + ")";
|
||||
|
||||
|
@ -30,7 +30,7 @@ public class HttpReport extends HttpEntityEnclosingRequestBaseHC4 {
|
||||
|
||||
setHeader("Content-Type", "text/xml; charset=UTF-8");
|
||||
setHeader("Accept", "text/xml");
|
||||
setHeader("Depth", "0");
|
||||
setHeader("Depth", "1");
|
||||
|
||||
try {
|
||||
setEntity(new StringEntity(entity, "UTF-8"));
|
||||
|
@ -114,9 +114,7 @@
|
||||
<string name="setup_querying_server">Contactant servidor. Espereu sisuplau.</string>
|
||||
<string name="setup_what_to_sync">Quines col·leccions s\'han de sincronitzar?</string>
|
||||
<string name="setup_address_books">Llibretes de contactes</string>
|
||||
<string name="setup_address_book">Llibreta de contactes</string>
|
||||
<string name="setup_calendars">Calendaris</string>
|
||||
<string name="setup_calendar">Calendari</string>
|
||||
<string name="setup_select_address_book">Seleccioneu com a molt una llibreta de contactes (Polseu de nou per deseleccionar):</string>
|
||||
<string name="setup_select_calendars">Seleccioneu els vostres calendaris:</string>
|
||||
|
||||
|
@ -113,9 +113,7 @@
|
||||
<string name="setup_querying_server">Probíhá komunikace se serverem. Chvilku strpení…</string>
|
||||
<string name="setup_what_to_sync">Které sbírky mají být synchronizovány?</string>
|
||||
<string name="setup_address_books">Adresáře</string>
|
||||
<string name="setup_address_book">Adresář</string>
|
||||
<string name="setup_calendars">Kalendáře</string>
|
||||
<string name="setup_calendar">Kalendář</string>
|
||||
<string name="setup_select_address_book">Vybrat alespoň jeden adresář (opakovaně tapnout pro odebrání):</string>
|
||||
<string name="setup_select_calendars">Vybrat své kalendáře:</string>
|
||||
|
||||
|
@ -10,7 +10,6 @@
|
||||
<string name="exception_http">HTTP-Fehler: %s</string>
|
||||
<string name="exception_incapable_resource">Fehlende Server-Unterstützung: %s</string>
|
||||
<string name="exception_io">E/A-Fehler: %s</string>
|
||||
<string name="exception_peer_unverified">Zertifikat ist nicht für \"%s\" ausgestellt</string>
|
||||
<string name="exception_uri_syntax">URI ungültig: %s</string>
|
||||
|
||||
<!-- MainActivity -->
|
||||
@ -110,9 +109,7 @@
|
||||
<string name="setup_querying_server">Daten werden vom Server abgefragt. Bitte warten…</string>
|
||||
<string name="setup_what_to_sync">Welche Ordner sollen synchronisiert werden?</string>
|
||||
<string name="setup_address_books">Adressbücher</string>
|
||||
<string name="setup_address_book">Adressbuch</string>
|
||||
<string name="setup_calendars">Kalender</string>
|
||||
<string name="setup_calendar">Kalender</string>
|
||||
<string name="setup_select_address_book">Ein oder kein Adressbuch auswählen (nochmal berühren, um abzuwählen):</string>
|
||||
<string name="setup_select_calendars">Kalender zur Synchronisation auswählen:</string>
|
||||
|
||||
|
@ -117,9 +117,7 @@
|
||||
<string name="setup_querying_server">Kapcsolódás a szerverhez. Egy pillanat…</string>
|
||||
<string name="setup_what_to_sync">Melyik gyűjtemények legyenek szinkronizálva?</string>
|
||||
<string name="setup_address_books">Címjegyzékek</string>
|
||||
<string name="setup_address_book">Címjegyzék</string>
|
||||
<string name="setup_calendars">Naptárak</string>
|
||||
<string name="setup_calendar">Naptár</string>
|
||||
<string name="setup_select_address_book">Egy címjegyzék választható (a kijelölés visszavonása újbóli érintéssel vagy másik tétel kiválasztásával):</string>
|
||||
<string name="setup_select_calendars">Naptárak kiválasztása:</string>
|
||||
|
||||
|
@ -108,9 +108,7 @@
|
||||
<string name="setup_querying_server">Шаљем упит серверу. Сачекајте…</string>
|
||||
<string name="setup_what_to_sync">Које збирке да синхронизујем?</string>
|
||||
<string name="setup_address_books">Адресари</string>
|
||||
<string name="setup_address_book">Адресар</string>
|
||||
<string name="setup_calendars">Календари</string>
|
||||
<string name="setup_calendar">Календар</string>
|
||||
<string name="setup_select_address_book">Изаберите један адресар (додирните поново да поништите избор):</string>
|
||||
<string name="setup_select_calendars">Изаберите ваше календаре:</string>
|
||||
|
||||
|
@ -98,9 +98,7 @@
|
||||
<string name="setup_querying_server">正在请求,请稍等…</string>
|
||||
<string name="setup_what_to_sync">需要同步哪些集合?</string>
|
||||
<string name="setup_address_books">通讯录</string>
|
||||
<string name="setup_address_book">通讯录</string>
|
||||
<string name="setup_calendars">日历</string>
|
||||
<string name="setup_calendar">日历</string>
|
||||
<string name="setup_select_address_book">最多选择一个通讯录:(再次点按可取消选择)</string>
|
||||
<string name="setup_select_calendars">选择日历:</string>
|
||||
|
||||
|
@ -12,7 +12,6 @@
|
||||
<string name="exception_http">HTTP error: %s</string>
|
||||
<string name="exception_incapable_resource">Missing capabilities: %s</string>
|
||||
<string name="exception_io">I/O error: %s</string>
|
||||
<string name="exception_peer_unverified">Certificate not issued for \"%s\"</string>
|
||||
<string name="exception_uri_syntax">Invalid URI: %s</string>
|
||||
|
||||
<!-- MainActivity -->
|
||||
@ -114,9 +113,7 @@
|
||||
<string name="setup_querying_server">Querying server. Please wait…</string>
|
||||
<string name="setup_what_to_sync">Which collections shall be synchronized?</string>
|
||||
<string name="setup_address_books">Address books</string>
|
||||
<string name="setup_address_book">Address book</string>
|
||||
<string name="setup_calendars">Calendars</string>
|
||||
<string name="setup_calendar">Calendar</string>
|
||||
<string name="setup_select_address_book">Select up to one address book (tap again to unselect):</string>
|
||||
<string name="setup_select_calendars">Select your calendars:</string>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user